aboutsummaryrefslogtreecommitdiffstats
path: root/src/particles
diff options
context:
space:
mode:
authorMatthew Vogt <matthew.vogt@nokia.com>2012-04-11 16:54:33 +1000
committerQt by Nokia <qt-info@nokia.com>2012-04-16 01:25:54 +0200
commitf189b8934dc405cbd45258abc5aba2713428b1cc (patch)
treedd3d4f61c7337343705faf7a603585c287b8ae50 /src/particles
parente00c40a89a1172add896e86e805a7cd42a4ea0c5 (diff)
Extract Particles code to an independent library
QtQuick clients that do not use particles features should not have to load these classes. Task-number: QTBUG-25178 Change-Id: Ib15f7655dc4d821595e06f9160d2770375279027 Reviewed-by: Glenn Watson <glenn.watson@nokia.com>
Diffstat (limited to 'src/particles')
-rw-r--r--src/particles/particleresources/noise.pngbin0 -> 19477 bytes
-rw-r--r--src/particles/particles.pri70
-rw-r--r--src/particles/particles.pro40
-rw-r--r--src/particles/particles.qrc5
-rw-r--r--src/particles/qquickage.cpp112
-rw-r--r--src/particles/qquickage_p.h99
-rw-r--r--src/particles/qquickangledirection.cpp120
-rw-r--r--src/particles/qquickangledirection_p.h131
-rw-r--r--src/particles/qquickcumulativedirection.cpp70
-rw-r--r--src/particles/qquickcumulativedirection_p.h67
-rw-r--r--src/particles/qquickcustomaffector.cpp211
-rw-r--r--src/particles/qquickcustomaffector_p.h162
-rw-r--r--src/particles/qquickcustomparticle.cpp473
-rw-r--r--src/particles/qquickcustomparticle_p.h119
-rw-r--r--src/particles/qquickdirection.cpp64
-rw-r--r--src/particles/qquickdirection_p.h68
-rw-r--r--src/particles/qquickellipseextruder.cpp86
-rw-r--r--src/particles/qquickellipseextruder_p.h83
-rw-r--r--src/particles/qquickfriction.cpp111
-rw-r--r--src/particles/qquickfriction_p.h101
-rw-r--r--src/particles/qquickgravity.cpp97
-rw-r--r--src/particles/qquickgravity_p.h115
-rw-r--r--src/particles/qquickgroupgoal.cpp112
-rw-r--r--src/particles/qquickgroupgoal_p.h100
-rw-r--r--src/particles/qquickimageparticle.cpp1978
-rw-r--r--src/particles/qquickimageparticle_p.h446
-rw-r--r--src/particles/qquickitemparticle.cpp277
-rw-r--r--src/particles/qquickitemparticle_p.h141
-rw-r--r--src/particles/qquicklineextruder.cpp85
-rw-r--r--src/particles/qquicklineextruder_p.h77
-rw-r--r--src/particles/qquickmaskextruder.cpp141
-rw-r--r--src/particles/qquickmaskextruder_p.h94
-rw-r--r--src/particles/qquickparticleaffector.cpp279
-rw-r--r--src/particles/qquickparticleaffector_p.h199
-rw-r--r--src/particles/qquickparticleemitter.cpp495
-rw-r--r--src/particles/qquickparticleemitter_p.h350
-rw-r--r--src/particles/qquickparticleextruder.cpp71
-rw-r--r--src/particles/qquickparticleextruder_p.h71
-rw-r--r--src/particles/qquickparticlegroup.cpp143
-rw-r--r--src/particles/qquickparticlegroup_p.h112
-rw-r--r--src/particles/qquickparticlepainter.cpp165
-rw-r--r--src/particles/qquickparticlepainter_p.h137
-rw-r--r--src/particles/qquickparticlesmodule.cpp120
-rw-r--r--src/particles/qquickparticlesmodule_p.h61
-rw-r--r--src/particles/qquickparticlesystem.cpp1164
-rw-r--r--src/particles/qquickparticlesystem_p.h379
-rw-r--r--src/particles/qquickpointattractor.cpp162
-rw-r--r--src/particles/qquickpointattractor_p.h167
-rw-r--r--src/particles/qquickpointdirection.cpp86
-rw-r--r--src/particles/qquickpointdirection_p.h133
-rw-r--r--src/particles/qquickrectangleextruder.cpp87
-rw-r--r--src/particles/qquickrectangleextruder_p.h86
-rw-r--r--src/particles/qquickspritegoal.cpp153
-rw-r--r--src/particles/qquickspritegoal_p.h123
-rw-r--r--src/particles/qquicktargetdirection.cpp131
-rw-r--r--src/particles/qquicktargetdirection_p.h189
-rw-r--r--src/particles/qquicktrailemitter.cpp285
-rw-r--r--src/particles/qquicktrailemitter_p.h168
-rw-r--r--src/particles/qquickturbulence.cpp205
-rw-r--r--src/particles/qquickturbulence_p.h116
-rw-r--r--src/particles/qquickv8particledata.cpp505
-rw-r--r--src/particles/qquickv8particledata_p.h67
-rw-r--r--src/particles/qquickwander.cpp181
-rw-r--r--src/particles/qquickwander_p.h158
-rw-r--r--src/particles/qtquickparticlesglobal_p.h69
65 files changed, 12672 insertions, 0 deletions
diff --git a/src/particles/particleresources/noise.png b/src/particles/particleresources/noise.png
new file mode 100644
index 0000000000..3c723e1a5a
--- /dev/null
+++ b/src/particles/particleresources/noise.png
Binary files differ
diff --git a/src/particles/particles.pri b/src/particles/particles.pri
new file mode 100644
index 0000000000..3e083ab291
--- /dev/null
+++ b/src/particles/particles.pri
@@ -0,0 +1,70 @@
+HEADERS += \
+ $$PWD/qquickangledirection_p.h \
+ $$PWD/qquickcustomparticle_p.h \
+ $$PWD/qquickcustomaffector_p.h \
+ $$PWD/qquickellipseextruder_p.h \
+ $$PWD/qquicktrailemitter_p.h \
+ $$PWD/qquickfriction_p.h \
+ $$PWD/qquickgravity_p.h \
+ $$PWD/qquickimageparticle_p.h \
+ $$PWD/qquickitemparticle_p.h \
+ $$PWD/qquickage_p.h \
+ $$PWD/qquicklineextruder_p.h \
+ $$PWD/qquickmaskextruder_p.h \
+ $$PWD/qquickparticleaffector_p.h \
+ $$PWD/qquickparticleemitter_p.h \
+ $$PWD/qquickparticleextruder_p.h \
+ $$PWD/qquickparticlepainter_p.h \
+ $$PWD/qquickparticlesmodule_p.h \
+ $$PWD/qquickparticlesystem_p.h \
+ $$PWD/qquickpointattractor_p.h \
+ $$PWD/qquickpointdirection_p.h \
+ $$PWD/qquickspritegoal_p.h \
+ $$PWD/qquickdirection_p.h \
+ $$PWD/qquicktargetdirection_p.h \
+ $$PWD/qquickturbulence_p.h \
+ $$PWD/qquickwander_p.h \
+ $$PWD/qquickcumulativedirection_p.h \
+ $$PWD/qquickv8particledata_p.h \
+ $$PWD/qquickrectangleextruder_p.h \
+ $$PWD/qquickparticlegroup_p.h \
+ $$PWD/qquickgroupgoal_p.h \
+ $$PWD/qtquickparticlesglobal_p.h
+
+SOURCES += \
+ $$PWD/qquickangledirection.cpp \
+ $$PWD/qquickcustomparticle.cpp \
+ $$PWD/qquickcustomaffector.cpp \
+ $$PWD/qquickellipseextruder.cpp \
+ $$PWD/qquicktrailemitter.cpp \
+ $$PWD/qquickfriction.cpp \
+ $$PWD/qquickgravity.cpp \
+ $$PWD/qquickimageparticle.cpp \
+ $$PWD/qquickitemparticle.cpp \
+ $$PWD/qquickage.cpp \
+ $$PWD/qquicklineextruder.cpp \
+ $$PWD/qquickmaskextruder.cpp \
+ $$PWD/qquickparticleaffector.cpp \
+ $$PWD/qquickparticleemitter.cpp \
+ $$PWD/qquickparticleextruder.cpp \
+ $$PWD/qquickparticlepainter.cpp \
+ $$PWD/qquickparticlesmodule.cpp \
+ $$PWD/qquickparticlesystem.cpp \
+ $$PWD/qquickpointattractor.cpp \
+ $$PWD/qquickpointdirection.cpp \
+ $$PWD/qquickspritegoal.cpp \
+ $$PWD/qquickdirection.cpp \
+ $$PWD/qquicktargetdirection.cpp \
+ $$PWD/qquickturbulence.cpp \
+ $$PWD/qquickwander.cpp \
+ $$PWD/qquickcumulativedirection.cpp \
+ $$PWD/qquickv8particledata.cpp \
+ $$PWD/qquickrectangleextruder.cpp \
+ $$PWD/qquickparticlegroup.cpp \
+ $$PWD/qquickgroupgoal.cpp
+
+RESOURCES += \
+ $$PWD/particles.qrc
+
+
+
diff --git a/src/particles/particles.pro b/src/particles/particles.pro
new file mode 100644
index 0000000000..3aff6bbcc0
--- /dev/null
+++ b/src/particles/particles.pro
@@ -0,0 +1,40 @@
+load(qt_module)
+
+TARGET = QtQuickParticles
+
+CONFIG += module
+CONFIG += dll warn_on
+MODULE_PRI = ../../modules/qt_quickparticles.pri
+
+QT = core-private gui-private v8-private qml-private quick-private
+
+DEFINES += QT_BUILD_QUICKPARTICLES_LIB QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES
+win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS
+solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2
+
+exists("qqml_enable_gcov") {
+ QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors
+ LIBS += -lgcov
+}
+
+MODULE = quickparticles
+load(qt_module_config)
+
+include(particles.pri)
+
+mac {
+ # FIXME: this is a workaround for broken qmake logic in qtAddModule()
+ # This function refuses to use frameworks unless the framework exists on
+ # the filesystem at the time qmake is run, resulting in a build failure
+ # if QtQuick is qmaked before QtQml is built and frameworks are
+ # in use. qtAddLibrary() contains correct logic to deal with this, so
+ # we'll explicitly call that for now.
+ load(qt)
+ LIBS -= -lQtQml # in non-framework builds, these should be re-added
+ LIBS -= -lQtQml_debug # within the qtAddLibrary if appropriate, so no
+ qtAddLibrary(QtQml) # harm done :)
+ LIBS -= -lQtQuick
+ LIBS -= -lQtQuick_debug
+ qtAddLibrary(QtQuick)
+}
+
diff --git a/src/particles/particles.qrc b/src/particles/particles.qrc
new file mode 100644
index 0000000000..344f9489a4
--- /dev/null
+++ b/src/particles/particles.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>particleresources/noise.png</file>
+ </qresource>
+</RCC>
diff --git a/src/particles/qquickage.cpp b/src/particles/qquickage.cpp
new file mode 100644
index 0000000000..9f24cba3cc
--- /dev/null
+++ b/src/particles/qquickage.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickage_p.h"
+#include "qquickparticleemitter_p.h"
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Age QQuickAgeAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Age affector allows you to prematurely age particles
+
+ The Age affector allows you to alter where the particle is in its lifecycle. Common uses
+ are to expire particles prematurely, possibly giving them time to animate out.
+
+ The Age affector only applies to particles which are still alive.
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::Age::lifeLeft
+
+ The amount of life to set the particle to have. Affected particles
+ will advance to a point in their life where they will have this many
+ milliseconds left to live.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::Age::advancePosition
+
+ advancePosition determines whether position, veclocity and acceleration are included in
+ the simulated aging done by the affector. If advancePosition is false,
+ then the position, velocity and acceleration will remain the same and only
+ other attributes (such as opacity) will advance in the simulation to where
+ it would normally be for that point in the particle's life. With advancePosition set to
+ true the position, velocity and acceleration will also advance to where it would
+ normally be by that point in the particle's life, making it advance its position
+ on screen.
+
+ Default value is true.
+*/
+
+QQuickAgeAffector::QQuickAgeAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_lifeLeft(0), m_advancePosition(true)
+{
+}
+
+
+bool QQuickAgeAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ Q_UNUSED(dt);
+ if (d->stillAlive()){
+ qreal curT = (qreal)m_system->timeInt/1000.0;
+ qreal ttl = (qreal)m_lifeLeft/1000.0;
+ if (!m_advancePosition && ttl > 0){
+ qreal x = d->curX();
+ qreal vx = d->curVX();
+ qreal ax = d->curAX();
+ qreal y = d->curY();
+ qreal vy = d->curVY();
+ qreal ay = d->curAY();
+ d->t = curT - (d->lifeSpan - ttl);
+ d->setInstantaneousX(x);
+ d->setInstantaneousVX(vx);
+ d->setInstantaneousAX(ax);
+ d->setInstantaneousY(y);
+ d->setInstantaneousVY(vy);
+ d->setInstantaneousAY(ay);
+ } else {
+ d->t = curT - (d->lifeSpan - ttl);
+ }
+ return true;
+ }
+ return false;
+}
+QT_END_NAMESPACE
diff --git a/src/particles/qquickage_p.h b/src/particles/qquickage_p.h
new file mode 100644
index 0000000000..2ec8faae4b
--- /dev/null
+++ b/src/particles/qquickage_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef KILLAFFECTOR_H
+#define KILLAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAgeAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(int lifeLeft READ lifeLeft WRITE setLifeLeft NOTIFY lifeLeftChanged)
+ Q_PROPERTY(bool advancePosition READ advancePosition WRITE setAdvancePosition NOTIFY advancePositionChanged)
+
+public:
+ explicit QQuickAgeAffector(QQuickItem *parent = 0);
+
+ int lifeLeft() const
+ {
+ return m_lifeLeft;
+ }
+
+ bool advancePosition() const
+ {
+ return m_advancePosition;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+signals:
+ void lifeLeftChanged(int arg);
+ void advancePositionChanged(bool arg);
+
+public slots:
+ void setLifeLeft(int arg)
+ {
+ if (m_lifeLeft != arg) {
+ m_lifeLeft = arg;
+ emit lifeLeftChanged(arg);
+ }
+ }
+
+ void setAdvancePosition(bool arg)
+ {
+ if (m_advancePosition != arg) {
+ m_advancePosition = arg;
+ emit advancePositionChanged(arg);
+ }
+ }
+
+private:
+ int m_lifeLeft;
+ bool m_advancePosition;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // KILLAFFECTOR_H
diff --git a/src/particles/qquickangledirection.cpp b/src/particles/qquickangledirection.cpp
new file mode 100644
index 0000000000..e77c47362c
--- /dev/null
+++ b/src/particles/qquickangledirection.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickangledirection_p.h"
+#include <stdlib.h>
+#include <cmath>
+#ifdef Q_OS_QNX
+#include <math.h>
+#endif
+QT_BEGIN_NAMESPACE
+const qreal CONV = 0.017453292519943295;
+/*!
+ \qmlclass AngleDirection QQuickAngleDirection
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Direction
+ \brief The AngleDirection element allows you to specify a direction that varies in angle
+
+ The AngledDirection element allows both the specification of a direction by angle and magnitude,
+ as well as varying the parameters by angle or magnitude.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::AngleDirection::angle
+ This property specifies the base angle for the direction.
+ The angle of this direction will vary by no more than angleVariation
+ from this angle.
+
+ Angle is specified by degrees clockwise from straight right.
+
+ The default value is zero.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::AngleDirection::magnitude
+ This property specifies the base magnitude for the direction.
+ The magnitude of this direction will vary by no more than magnitudeVariation
+ from this magnitude.
+
+ Magnitude is specified in units of pixels per second.
+
+ The default value is zero.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::AngleDirection::angleVariation
+ This property specifies the maximum angle variation for the direction.
+ The angle of the direction will vary by up to angleVariation clockwise
+ and anticlockwise from the value specified in angle.
+
+ Angle is specified by degrees clockwise from straight right.
+
+ The default value is zero.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::AngleDirection::magnitudeVariation
+ This property specifies the base magnitude for the direction.
+ The magnitude of this direction will vary by no more than magnitudeVariation
+ from the base magnitude.
+
+ Magnitude is specified in units of pixels per second.
+
+ The default value is zero.
+*/
+QQuickAngleDirection::QQuickAngleDirection(QObject *parent) :
+ QQuickDirection(parent)
+ , m_angle(0)
+ , m_magnitude(0)
+ , m_angleVariation(0)
+ , m_magnitudeVariation(0)
+{
+
+}
+
+const QPointF QQuickAngleDirection::sample(const QPointF &from)
+{
+ Q_UNUSED(from);
+ QPointF ret;
+ qreal theta = m_angle*CONV - m_angleVariation*CONV + rand()/float(RAND_MAX) * m_angleVariation*CONV * 2;
+ qreal mag = m_magnitude- m_magnitudeVariation + rand()/float(RAND_MAX) * m_magnitudeVariation * 2;
+ ret.setX(mag * cos(theta));
+ ret.setY(mag * sin(theta));
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickangledirection_p.h b/src/particles/qquickangledirection_p.h
new file mode 100644
index 0000000000..84633cd40e
--- /dev/null
+++ b/src/particles/qquickangledirection_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQuickANGLEDDIRECTION_H
+#define QQuickANGLEDDIRECTION_H
+#include "qquickdirection_p.h"
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAngleDirection : public QQuickDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged)
+ Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged)
+ Q_PROPERTY(qreal angleVariation READ angleVariation WRITE setAngleVariation NOTIFY angleVariationChanged)
+ Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged)
+public:
+ explicit QQuickAngleDirection(QObject *parent = 0);
+ const QPointF sample(const QPointF &from);
+ qreal angle() const
+ {
+ return m_angle;
+ }
+
+ qreal magnitude() const
+ {
+ return m_magnitude;
+ }
+
+ qreal angleVariation() const
+ {
+ return m_angleVariation;
+ }
+
+ qreal magnitudeVariation() const
+ {
+ return m_magnitudeVariation;
+ }
+
+signals:
+
+ void angleChanged(qreal arg);
+
+ void magnitudeChanged(qreal arg);
+
+ void angleVariationChanged(qreal arg);
+
+ void magnitudeVariationChanged(qreal arg);
+
+public slots:
+void setAngle(qreal arg)
+{
+ if (m_angle != arg) {
+ m_angle = arg;
+ emit angleChanged(arg);
+ }
+}
+
+void setMagnitude(qreal arg)
+{
+ if (m_magnitude != arg) {
+ m_magnitude = arg;
+ emit magnitudeChanged(arg);
+ }
+}
+
+void setAngleVariation(qreal arg)
+{
+ if (m_angleVariation != arg) {
+ m_angleVariation = arg;
+ emit angleVariationChanged(arg);
+ }
+}
+
+void setMagnitudeVariation(qreal arg)
+{
+ if (m_magnitudeVariation != arg) {
+ m_magnitudeVariation = arg;
+ emit magnitudeVariationChanged(arg);
+ }
+}
+
+private:
+qreal m_angle;
+qreal m_magnitude;
+qreal m_angleVariation;
+qreal m_magnitudeVariation;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // QQuickANGLEDDIRECTION_H
diff --git a/src/particles/qquickcumulativedirection.cpp b/src/particles/qquickcumulativedirection.cpp
new file mode 100644
index 0000000000..d7c4094297
--- /dev/null
+++ b/src/particles/qquickcumulativedirection.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcumulativedirection_p.h"
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass CumulativeDirection QQuickCumulativeDirection
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Direction
+ \brief The CumulativeDirection element allows you to specify a direction made of other directions
+
+ The CumulativeDirection element will act as a direction that sums the directions within it.
+*/
+QQuickCumulativeDirection::QQuickCumulativeDirection(QObject *parent):QQuickDirection(parent)
+{
+}
+
+QQmlListProperty<QQuickDirection> QQuickCumulativeDirection::directions()
+{
+ return QQmlListProperty<QQuickDirection>(this, m_directions);//TODO: Proper list property
+}
+
+const QPointF QQuickCumulativeDirection::sample(const QPointF &from)
+{
+ QPointF ret;
+ foreach (QQuickDirection* dir, m_directions)
+ ret += dir->sample(from);
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickcumulativedirection_p.h b/src/particles/qquickcumulativedirection_p.h
new file mode 100644
index 0000000000..e1675a18d8
--- /dev/null
+++ b/src/particles/qquickcumulativedirection_p.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQuickCUMULATIVEDIRECTION_P_H
+#define QQuickCUMULATIVEDIRECTION_P_H
+#include "qquickdirection_p.h"
+#include <QQmlListProperty>
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickCumulativeDirection : public QQuickDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<QQuickDirection> directions READ directions)
+ Q_CLASSINFO("DefaultProperty", "directions")
+public:
+ explicit QQuickCumulativeDirection(QObject *parent = 0);
+ QQmlListProperty<QQuickDirection> directions();
+ const QPointF sample(const QPointF &from);
+private:
+ QList<QQuickDirection*> m_directions;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QQuickCUMULATIVEDIRECTION_P_H
diff --git a/src/particles/qquickcustomaffector.cpp b/src/particles/qquickcustomaffector.cpp
new file mode 100644
index 0000000000..acec98192d
--- /dev/null
+++ b/src/particles/qquickcustomaffector.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcustomaffector_p.h"
+#include <private/qv8engine_p.h>
+#include <private/qqmlengine_p.h>
+#include <QQmlEngine>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+
+//TODO: Move docs (and inheritence) to real base when docs can propagate. Currently this pretends to be the base class!
+/*!
+ \qmlsignal QtQuick.Particles2::Affector::affectParticles(Array particles, real dt)
+
+ This handler is called when particles are selected to be affected. particles contains
+ an array of particle objects which can be directly manipulated.
+
+ dt is the time since the last time it was affected. Use dt to normalize
+ trajectory manipulations to real time.
+
+ Note that JS is slower to execute, so it is not recommended to use this in
+ high-volume particle systems.
+*/
+
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Affector::position
+
+ Affected particles will have their position set to this direction,
+ relative to the ParticleSystem. When interpreting directions as points,
+ imagine it as an arrow with the base at the 0,0 of the ParticleSystem and the
+ tip at where the specified position will be.
+*/
+
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Affector::speed
+
+ Affected particles will have their speed set to this direction.
+*/
+
+
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Affector::acceleration
+
+ Affected particles will have their acceleration set to this direction.
+*/
+
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::Affector::relative
+
+ Whether the affected particles have their existing position/speed/acceleration added
+ to the new one.
+
+ Default is true.
+*/
+QQuickCustomAffector::QQuickCustomAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent)
+ , m_position(&m_nullVector)
+ , m_speed(&m_nullVector)
+ , m_acceleration(&m_nullVector)
+ , m_relative(true)
+{
+}
+
+bool QQuickCustomAffector::isAffectConnected()
+{
+ static int idx = QObjectPrivate::get(this)->signalIndex("affectParticles(QQmlV8Handle,qreal)");
+ return QObjectPrivate::get(this)->isSignalConnected(idx);
+}
+
+void QQuickCustomAffector::affectSystem(qreal dt)
+{
+ if (!isAffectConnected()) {
+ QQuickParticleAffector::affectSystem(dt);
+ return;
+ }
+ if (!m_enabled)
+ return;
+ updateOffsets();
+
+ QList<QQuickParticleData*> toAffect;
+ foreach (QQuickParticleGroupData* gd, m_system->groupData)
+ if (activeGroup(m_system->groupData.key(gd)))
+ foreach (QQuickParticleData* d, gd->data)
+ if (shouldAffect(d))
+ toAffect << d;
+
+ if (toAffect.isEmpty())
+ return;
+
+ if (m_onceOff)
+ dt = 1.0;
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(QQmlEnginePrivate::getV8Engine(qmlEngine(this))->context());
+ v8::Handle<v8::Array> array = v8::Array::New(toAffect.size());
+ for (int i=0; i<toAffect.size(); i++)
+ array->Set(i, toAffect[i]->v8Value().toHandle());
+
+ if (dt >= simulationCutoff || dt <= simulationDelta) {
+ affectProperties(toAffect, dt);
+ emit affectParticles(QQmlV8Handle::fromHandle(array), dt);
+ } else {
+ int realTime = m_system->timeInt;
+ m_system->timeInt -= dt * 1000.0;
+ while (dt > simulationDelta) {
+ m_system->timeInt += simulationDelta * 1000.0;
+ dt -= simulationDelta;
+ affectProperties(toAffect, simulationDelta);
+ emit affectParticles(QQmlV8Handle::fromHandle(array), simulationDelta);
+ }
+ m_system->timeInt = realTime;
+ if (dt > 0.0) {
+ affectProperties(toAffect, dt);
+ emit affectParticles(QQmlV8Handle::fromHandle(array), dt);
+ }
+ }
+
+ foreach (QQuickParticleData* d, toAffect)
+ if (d->update == 1.0)
+ postAffect(d);
+}
+
+bool QQuickCustomAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ //This does the property based affecting, called by superclass if signal isn't hooked up.
+ bool changed = false;
+ QPointF curPos(d->curX(), d->curY());
+
+ if (m_acceleration != &m_nullVector){
+ QPointF pos = m_acceleration->sample(curPos);
+ if (m_relative) {
+ pos *= dt;
+ pos += QPointF(d->curAX(), d->curAY());
+ }
+ d->setInstantaneousAX(pos.x());
+ d->setInstantaneousAY(pos.y());
+ changed = true;
+ }
+
+ if (m_speed != &m_nullVector){
+ QPointF pos = m_speed->sample(curPos);
+ if (m_relative) {
+ pos *= dt;
+ pos += QPointF(d->curVX(), d->curVY());
+ }
+ d->setInstantaneousVX(pos.x());
+ d->setInstantaneousVY(pos.y());
+ changed = true;
+ }
+
+ if (m_position != &m_nullVector){
+ QPointF pos = m_position->sample(curPos);
+ if (m_relative) {
+ pos *= dt;
+ pos += curPos;
+ }
+ d->setInstantaneousX(pos.x());
+ d->setInstantaneousY(pos.y());
+ changed = true;
+ }
+
+ return changed;
+}
+
+void QQuickCustomAffector::affectProperties(const QList<QQuickParticleData*> particles, qreal dt)
+{
+ foreach (QQuickParticleData* d, particles)
+ if ( affectParticle(d, dt) )
+ d->update = 1.0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickcustomaffector_p.h b/src/particles/qquickcustomaffector_p.h
new file mode 100644
index 0000000000..1266830f94
--- /dev/null
+++ b/src/particles/qquickcustomaffector_p.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CUSTOMAFFECTOR_H
+#define CUSTOMAFFECTOR_H
+
+#include <QObject>
+#include "qquickparticlesystem_p.h"
+#include "qquickparticleextruder_p.h"
+#include "qquickparticleaffector_p.h"
+#include "qquickdirection_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickCustomAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(bool relative READ relative WRITE setRelative NOTIFY relativeChanged)
+ Q_PROPERTY(QQuickDirection *position READ position WRITE setPosition NOTIFY positionChanged RESET positionReset)
+ Q_PROPERTY(QQuickDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged RESET speedReset)
+ Q_PROPERTY(QQuickDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged RESET accelerationReset)
+
+public:
+ explicit QQuickCustomAffector(QQuickItem *parent = 0);
+ virtual void affectSystem(qreal dt);
+
+ QQuickDirection * position() const
+ {
+ return m_position;
+ }
+
+ QQuickDirection * speed() const
+ {
+ return m_speed;
+ }
+
+ QQuickDirection * acceleration() const
+ {
+ return m_acceleration;
+ }
+
+ void positionReset()
+ {
+ m_position = &m_nullVector;
+ }
+
+ void speedReset()
+ {
+ m_speed = &m_nullVector;
+ }
+
+ void accelerationReset()
+ {
+ m_acceleration = &m_nullVector;
+ }
+
+ bool relative() const
+ {
+ return m_relative;
+ }
+
+
+signals:
+ void affectParticles(QQmlV8Handle particles, qreal dt);
+
+ void positionChanged(QQuickDirection * arg);
+
+ void speedChanged(QQuickDirection * arg);
+
+ void accelerationChanged(QQuickDirection * arg);
+
+ void relativeChanged(bool arg);
+
+public slots:
+ void setPosition(QQuickDirection * arg)
+ {
+ if (m_position != arg) {
+ m_position = arg;
+ emit positionChanged(arg);
+ }
+ }
+
+ void setSpeed(QQuickDirection * arg)
+ {
+ if (m_speed != arg) {
+ m_speed = arg;
+ emit speedChanged(arg);
+ }
+ }
+
+ void setAcceleration(QQuickDirection * arg)
+ {
+ if (m_acceleration != arg) {
+ m_acceleration = arg;
+ emit accelerationChanged(arg);
+ }
+ }
+
+ void setRelative(bool arg)
+ {
+ if (m_relative != arg) {
+ m_relative = arg;
+ emit relativeChanged(arg);
+ }
+ }
+
+protected:
+ bool isAffectConnected();
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+private:
+ void affectProperties(const QList<QQuickParticleData*> particles, qreal dt);
+ QQuickDirection * m_position;
+ QQuickDirection * m_speed;
+ QQuickDirection * m_acceleration;
+
+ QQuickDirection m_nullVector;
+ bool m_relative;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // CUSTOMAFFECTOR_H
diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp
new file mode 100644
index 0000000000..7d27e48c6b
--- /dev/null
+++ b/src/particles/qquickcustomparticle.cpp
@@ -0,0 +1,473 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcustomparticle_p.h"
+#include <QtQuick/private/qquickshadereffectmesh_p.h>
+#include <cstdlib>
+
+QT_BEGIN_NAMESPACE
+
+//Includes comments because the code isn't self explanatory
+static const char qt_particles_template_vertex_code[] =
+ "attribute highp vec2 qt_ParticlePos;\n"
+ "attribute highp vec2 qt_ParticleTex;\n"
+ "attribute highp vec4 qt_ParticleData; // x = time, y = lifeSpan, z = size, w = endSize\n"
+ "attribute highp vec4 qt_ParticleVec; // x,y = constant speed, z,w = acceleration\n"
+ "attribute highp float qt_ParticleR;\n"
+ "uniform highp mat4 qt_Matrix;\n"
+ "uniform highp float qt_Timestamp;\n"
+ "varying highp vec2 qt_TexCoord0;\n"
+ "void defaultMain() {\n"
+ " qt_TexCoord0 = qt_ParticleTex;\n"
+ " highp float size = qt_ParticleData.z;\n"
+ " highp float endSize = qt_ParticleData.w;\n"
+ " highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;\n"
+ " highp float currentSize = mix(size, endSize, t * t);\n"
+ " if (t < 0. || t > 1.)\n"
+ " currentSize = 0.;\n"
+ " highp vec2 pos = qt_ParticlePos\n"
+ " - currentSize / 2. + currentSize * qt_ParticleTex // adjust size\n"
+ " + qt_ParticleVec.xy * t * qt_ParticleData.y // apply speed vector..\n"
+ " + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);\n"
+ " gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n"
+ "}";
+static const char qt_particles_default_vertex_code[] =
+ "void main() { \n"
+ " defaultMain(); \n"
+ "}";
+
+static const char qt_particles_default_fragment_code[] =
+ "uniform sampler2D source; \n"
+ "varying highp vec2 qt_TexCoord0; \n"
+ "uniform lowp float qt_Opacity; \n"
+ "void main() { \n"
+ " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
+ "}";
+
+static QSGGeometry::Attribute PlainParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
+ QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // TexCoord
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(3, 4, GL_FLOAT), // Vectors
+ QSGGeometry::Attribute::create(4, 1, GL_FLOAT) // r
+};
+
+static QSGGeometry::AttributeSet PlainParticle_AttributeSet =
+{
+ 5, // Attribute Count
+ (2 + 2 + 4 + 4 + 1) * sizeof(float),
+ PlainParticle_Attributes
+};
+
+struct PlainVertex {
+ float x;
+ float y;
+ float tx;
+ float ty;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+ float r;
+};
+
+struct PlainVertices {
+ PlainVertex v1;
+ PlainVertex v2;
+ PlainVertex v3;
+ PlainVertex v4;
+};
+
+/*!
+ \qmlclass CustomParticle QQuickCustomParticle
+ \inqmlmodule QtQuick.Particles 2
+ \inherits ParticlePainter
+ \brief The CustomParticle element allows you to specify your own shader to paint particles.
+
+*/
+
+QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
+ : QQuickParticlePainter(parent)
+ , m_dirtyUniforms(true)
+ , m_dirtyUniformValues(true)
+ , m_dirtyTextureProviders(true)
+ , m_dirtyProgram(true)
+{
+ setFlag(QQuickItem::ItemHasContents);
+}
+
+void QQuickCustomParticle::sceneGraphInvalidated()
+{
+ m_nodes.clear();
+}
+
+QQuickCustomParticle::~QQuickCustomParticle()
+{
+}
+
+void QQuickCustomParticle::componentComplete()
+{
+ m_common.updateShader(this, Key::FragmentShader);
+ updateVertexShader();
+ reset();
+ QQuickParticlePainter::componentComplete();
+}
+
+
+//Trying to keep the shader conventions the same as in qsgshadereffectitem
+/*!
+ \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader
+
+ This property holds the fragment shader's GLSL source code.
+ The default shader expects the texture coordinate to be passed from the
+ vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
+ sampler2D named "source".
+*/
+
+void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
+{
+ if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
+ return;
+ m_common.source.sourceCode[Key::FragmentShader] = code;
+ m_dirtyProgram = true;
+ if (isComponentComplete()) {
+ m_common.updateShader(this, Key::FragmentShader);
+ reset();
+ }
+ emit fragmentShaderChanged();
+}
+
+/*!
+ \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader
+
+ This property holds the vertex shader's GLSL source code.
+
+ The default shader passes the texture coordinate along to the fragment
+ shader as "varying highp vec2 qt_TexCoord0".
+
+ To aid writing a particle vertex shader, the following GLSL code is prepended
+ to your vertex shader:
+ \code
+ attribute highp vec2 qt_ParticlePos;
+ attribute highp vec2 qt_ParticleTex;
+ attribute highp vec4 qt_ParticleData; // x = time, y = lifeSpan, z = size, w = endSize
+ attribute highp vec4 qt_ParticleVec; // x,y = constant speed, z,w = acceleration
+ attribute highp float qt_ParticleR;
+ uniform highp mat4 qt_Matrix;
+ uniform highp float qt_Timestamp;
+ varying highp vec2 qt_TexCoord0;
+ void defaultMain() {
+ qt_TexCoord0 = qt_ParticleTex;
+ highp float size = qt_ParticleData.z;
+ highp float endSize = qt_ParticleData.w;
+ highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;
+ highp float currentSize = mix(size, endSize, t * t);
+ if (t < 0. || t > 1.)
+ currentSize = 0.;
+ highp vec2 pos = qt_ParticlePos
+ - currentSize / 2. + currentSize * qt_ParticleTex // adjust size
+ + qt_ParticleVec.xy * t * qt_ParticleData.y // apply speed vector..
+ + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);
+ gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);
+ }
+ \endcode
+
+ defaultMain() is the same code as in the default shader, you can call this for basic
+ particle functions and then add additional variables for custom effects. Note that
+ the vertex shader for particles is responsible for simulating the movement of particles
+ over time, the particle data itself only has the starting position and spawn time.
+*/
+
+void QQuickCustomParticle::setVertexShader(const QByteArray &code)
+{
+ if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
+ return;
+ m_common.source.sourceCode[Key::VertexShader] = code;
+
+ m_dirtyProgram = true;
+ if (isComponentComplete()) {
+ updateVertexShader();
+ reset();
+ }
+ emit vertexShaderChanged();
+}
+
+void QQuickCustomParticle::updateVertexShader()
+{
+ m_common.disconnectPropertySignals(this, Key::VertexShader);
+ qDeleteAll(m_common.signalMappers[Key::VertexShader]);
+ m_common.uniformData[Key::VertexShader].clear();
+ m_common.signalMappers[Key::VertexShader].clear();
+ m_common.attributes.clear();
+ m_common.attributes.append("qt_ParticlePos");
+ m_common.attributes.append("qt_ParticleTex");
+ m_common.attributes.append("qt_ParticleData");
+ m_common.attributes.append("qt_ParticleVec");
+ m_common.attributes.append("qt_ParticleR");
+
+ UniformData d;
+ d.name = "qt_Matrix";
+ d.specialType = UniformData::Matrix;
+ m_common.uniformData[Key::VertexShader].append(d);
+ m_common.signalMappers[Key::VertexShader].append(0);
+
+ d.name = "qt_Timestamp";
+ d.specialType = UniformData::None;
+ m_common.uniformData[Key::VertexShader].append(d);
+ m_common.signalMappers[Key::VertexShader].append(0);
+
+ const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
+ if (!code.isEmpty())
+ m_common.lookThroughShaderCode(this, Key::VertexShader, code);
+
+ m_common.connectPropertySignals(this, Key::VertexShader);
+}
+
+void QQuickCustomParticle::reset()
+{
+ QQuickParticlePainter::reset();
+ update();
+}
+
+QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
+ if (m_pleaseReset){
+ delete rootNode;//Automatically deletes children
+ rootNode = 0;
+ m_nodes.clear();
+ m_pleaseReset = false;
+ m_dirtyProgram = true;
+ }
+
+ if (m_system && m_system->isRunning() && !m_system->isPaused()){
+ rootNode = prepareNextFrame(rootNode);
+ if (rootNode)
+ update();
+ }
+
+ return rootNode;
+}
+
+QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
+{
+ if (!rootNode)
+ rootNode = buildCustomNodes();
+
+ if (!rootNode)
+ return 0;
+
+ if (m_dirtyProgram) {
+ QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
+ Q_ASSERT(material);
+
+ Key s = m_common.source;
+ if (s.sourceCode[Key::FragmentShader].isEmpty())
+ s.sourceCode[Key::FragmentShader] = qt_particles_default_fragment_code;
+ if (s.sourceCode[Key::VertexShader].isEmpty())
+ s.sourceCode[Key::VertexShader] = qt_particles_default_vertex_code;
+ s.sourceCode[Key::VertexShader] = qt_particles_template_vertex_code + s.sourceCode[Key::VertexShader];
+ s.className = metaObject()->className();
+
+ material->setProgramSource(s);
+ material->attributes = m_common.attributes;
+ foreach (QQuickShaderEffectNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ m_dirtyProgram = false;
+ m_dirtyUniforms = true;
+ }
+
+ m_lastTime = m_system->systemSync(this) / 1000.;
+ if (true) //Currently this is how we update timestamp... potentially over expensive.
+ buildData(rootNode);
+ return rootNode;
+}
+
+QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
+{
+#ifdef QT_OPENGL_ES_2
+ if (m_count * 4 > 0xffff) {
+ printf("CustomParticle: Too many particles... \n");
+ return 0;
+ }
+#endif
+
+ if (m_count <= 0) {
+ printf("CustomParticle: Too few particles... \n");
+ return 0;
+ }
+
+ if (m_groups.isEmpty())
+ return 0;
+
+ QQuickShaderEffectNode *rootNode = 0;
+ QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
+ m_dirtyProgram = true;
+
+ foreach (const QString &str, m_groups){
+ int gIdx = m_system->groupIds[str];
+ int count = m_system->groupData[gIdx]->size();
+
+ QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
+ m_nodes.insert(gIdx, node);
+
+ node->setMaterial(material);
+
+ //Create Particle Geometry
+ int vCount = count * 4;
+ int iCount = count * 6;
+ QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount);
+ g->setDrawingMode(GL_TRIANGLES);
+ node->setGeometry(g);
+ node->setFlag(QSGNode::OwnsGeometry, true);
+ PlainVertex *vertices = (PlainVertex *) g->vertexData();
+ for (int p=0; p < count; ++p) {
+ commit(gIdx, p);
+ vertices[0].tx = 0;
+ vertices[0].ty = 0;
+
+ vertices[1].tx = 1;
+ vertices[1].ty = 0;
+
+ vertices[2].tx = 0;
+ vertices[2].ty = 1;
+
+ vertices[3].tx = 1;
+ vertices[3].ty = 1;
+ vertices += 4;
+ }
+ quint16 *indices = g->indexDataAsUShort();
+ for (int i=0; i < count; ++i) {
+ int o = i * 4;
+ indices[0] = o;
+ indices[1] = o + 1;
+ indices[2] = o + 2;
+ indices[3] = o + 1;
+ indices[4] = o + 3;
+ indices[5] = o + 2;
+ indices += 6;
+ }
+ }
+
+ QHash<int, QQuickShaderEffectNode*>::const_iterator it = m_nodes.begin();
+ rootNode = it.value();
+ rootNode->setFlag(QSGNode::OwnsMaterial, true);
+ for (++it; it != m_nodes.end(); ++it)
+ rootNode->appendChildNode(it.value());
+
+ return rootNode;
+}
+
+void QQuickCustomParticle::sourceDestroyed(QObject *object)
+{
+ m_common.sourceDestroyed(object);
+}
+
+void QQuickCustomParticle::propertyChanged(int mappedId)
+{
+ bool textureProviderChanged;
+ m_common.propertyChanged(this, mappedId, &textureProviderChanged);
+ m_dirtyTextureProviders |= textureProviderChanged;
+ m_dirtyUniformValues = true;
+ update();
+}
+
+
+void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
+{
+ if (!rootNode)
+ return;
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
+ if (m_common.uniformData[shaderType].at(i).name == "qt_Timestamp")
+ m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime);
+ }
+ }
+ m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()),
+ m_dirtyUniforms, true, m_dirtyTextureProviders);
+ foreach (QQuickShaderEffectNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
+ m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
+}
+
+void QQuickCustomParticle::initialize(int gIdx, int pIdx)
+{
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ datum->r = rand()/(qreal)RAND_MAX;
+}
+
+void QQuickCustomParticle::commit(int gIdx, int pIdx)
+{
+ if (m_nodes[gIdx] == 0)
+ return;
+
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ PlainVertices *particles = (PlainVertices *) m_nodes[gIdx]->geometry()->vertexData();
+ PlainVertex *vertices = (PlainVertex *)&particles[pIdx];
+ for (int i=0; i<4; ++i) {
+ vertices[i].x = datum->x - m_systemOffset.x();
+ vertices[i].y = datum->y - m_systemOffset.y();
+ vertices[i].t = datum->t;
+ vertices[i].lifeSpan = datum->lifeSpan;
+ vertices[i].size = datum->size;
+ vertices[i].endSize = datum->endSize;
+ vertices[i].vx = datum->vx;
+ vertices[i].vy = datum->vy;
+ vertices[i].ax = datum->ax;
+ vertices[i].ay = datum->ay;
+ vertices[i].r = datum->r;
+ }
+}
+
+void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ if (change == QQuickItem::ItemSceneChange)
+ m_common.updateCanvas(value.canvas);
+ QQuickParticlePainter::itemChange(change, value);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickcustomparticle_p.h b/src/particles/qquickcustomparticle_p.h
new file mode 100644
index 0000000000..f689091268
--- /dev/null
+++ b/src/particles/qquickcustomparticle_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CUSTOM_PARTICLE_H
+#define CUSTOM_PARTICLE_H
+#include "qquickparticlepainter_p.h"
+#include <private/qquickshadereffectnode_p.h>
+#include <private/qquickshadereffect_p.h>
+#include <QSignalMapper>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QSGNode;
+struct PlainVertices;
+
+class QQuickShaderEffectMaterialObject;
+
+//Genealogy: Hybrid of UltraParticle and ShaderEffect
+class QQuickCustomParticle : public QQuickParticlePainter
+{
+ Q_OBJECT
+ Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged)
+ Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged)
+
+public:
+ explicit QQuickCustomParticle(QQuickItem* parent=0);
+ ~QQuickCustomParticle();
+
+ QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; }
+ void setFragmentShader(const QByteArray &code);
+
+ QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; }
+ void setVertexShader(const QByteArray &code);
+
+Q_SIGNALS:
+ void fragmentShaderChanged();
+ void vertexShaderChanged();
+
+protected:
+ virtual void initialize(int gIdx, int pIdx);
+ virtual void commit(int gIdx, int pIdx);
+
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+ QQuickShaderEffectNode *prepareNextFrame(QQuickShaderEffectNode *rootNode);
+ void reset();
+ void resize(int oldCount, int newCount);
+ virtual void componentComplete();
+ QQuickShaderEffectNode *buildCustomNodes();
+
+ void sceneGraphInvalidated();
+ void itemChange(ItemChange change, const ItemChangeData &value);
+
+private Q_SLOTS:
+ void sourceDestroyed(QObject *object);
+ void propertyChanged(int mappedId);
+
+private:
+ typedef QQuickShaderEffectMaterialKey Key;
+ typedef QQuickShaderEffectMaterial::UniformData UniformData;
+
+ void buildData(QQuickShaderEffectNode *rootNode);
+ void updateVertexShader();
+
+ QQuickShaderEffectCommon m_common;
+
+ QHash<int, QQuickShaderEffectNode*> m_nodes;
+ qreal m_lastTime;
+
+ uint m_dirtyUniforms : 1;
+ uint m_dirtyUniformValues : 1;
+ uint m_dirtyTextureProviders : 1;
+ uint m_dirtyProgram : 1;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //HEADER_GUARD
diff --git a/src/particles/qquickdirection.cpp b/src/particles/qquickdirection.cpp
new file mode 100644
index 0000000000..4127d06be0
--- /dev/null
+++ b/src/particles/qquickdirection.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickdirection_p.h"
+
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Direction QQuickDirection
+ \inqmlmodule QtQuick.Particles 2
+ \brief The Direction elements allow you to specify a vector space.
+
+*/
+
+
+QQuickDirection::QQuickDirection(QObject *parent) :
+ QObject(parent)
+{
+}
+
+const QPointF QQuickDirection::sample(const QPointF &from)
+{
+ Q_UNUSED(from);
+ return QPointF();
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickdirection_p.h b/src/particles/qquickdirection_p.h
new file mode 100644
index 0000000000..651865a1f5
--- /dev/null
+++ b/src/particles/qquickdirection_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef VARYINGVECTOR_H
+#define VARYINGVECTOR_H
+
+#include <QObject>
+#include <QPointF>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickDirection : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QQuickDirection(QObject *parent = 0);
+
+ virtual const QPointF sample(const QPointF &from);
+signals:
+
+public slots:
+
+protected:
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // VARYINGVECTOR_H
diff --git a/src/particles/qquickellipseextruder.cpp b/src/particles/qquickellipseextruder.cpp
new file mode 100644
index 0000000000..083564e5cb
--- /dev/null
+++ b/src/particles/qquickellipseextruder.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickellipseextruder_p.h"
+#include <stdlib.h>
+#include <cmath>
+
+#ifdef Q_OS_QNX
+#include <math.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass EllipseShape QQuickEllipseExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Shape
+ \brief The EllipseShape represents an ellipse to other particle system elements
+
+ This shape can be used by Emitter subclasses and Affector subclasses to have
+ them act upon an ellipse shaped area.
+*/
+QQuickEllipseExtruder::QQuickEllipseExtruder(QObject *parent) :
+ QQuickParticleExtruder(parent)
+ , m_fill(true)
+{
+}
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::EllipseShape::fill
+ If fill is true the ellipse is filled; otherwise it is just a border.
+
+ Default is true.
+*/
+
+QPointF QQuickEllipseExtruder::extrude(const QRectF & r)
+{
+ qreal theta = ((qreal)rand()/RAND_MAX) * 6.2831853071795862;
+ qreal mag = m_fill ? ((qreal)rand()/RAND_MAX) : 1;
+ return QPointF(r.x() + r.width()/2 + mag * (r.width()/2) * cos(theta),
+ r.y() + r.height()/2 + mag * (r.height()/2) * sin(theta));
+}
+
+bool QQuickEllipseExtruder::contains(const QRectF &bounds, const QPointF &point)
+{
+ return bounds.contains(point);//TODO: Ellipse
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickellipseextruder_p.h b/src/particles/qquickellipseextruder_p.h
new file mode 100644
index 0000000000..c2d0c634ab
--- /dev/null
+++ b/src/particles/qquickellipseextruder_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ELLIPSEEXTRUDER_H
+#define ELLIPSEEXTRUDER_H
+#include "qquickparticleextruder_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickEllipseExtruder : public QQuickParticleExtruder
+{
+ Q_OBJECT
+ Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Use base class? If it's still box
+public:
+ explicit QQuickEllipseExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ virtual bool contains(const QRectF &bounds, const QPointF &point);
+
+ bool fill() const
+ {
+ return m_fill;
+ }
+
+signals:
+
+ void fillChanged(bool arg);
+
+public slots:
+
+ void setFill(bool arg)
+ {
+ if (m_fill != arg) {
+ m_fill = arg;
+ emit fillChanged(arg);
+ }
+ }
+private:
+ bool m_fill;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // ELLIPSEEXTRUDER_H
diff --git a/src/particles/qquickfriction.cpp b/src/particles/qquickfriction.cpp
new file mode 100644
index 0000000000..d37d109f48
--- /dev/null
+++ b/src/particles/qquickfriction.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickfriction_p.h"
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Friction QQuickFrictionAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Friction affector slows down movement proportional to the particle's current speed.
+
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Friction::factor
+
+ A drag will be applied to moving objects which is this factor of their current velocity.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Friction::threshold
+
+ The drag will only be applied to objects with a velocity above the threshold velocity. The
+ drag applied will bring objects down to the threshold velocity, but no further.
+
+ The default threshold is 0
+*/
+static qreal sign(qreal a)
+{
+ return a >= 0 ? 1 : -1;
+}
+
+static const qreal epsilon = 0.00001;
+
+QQuickFrictionAffector::QQuickFrictionAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_factor(0.0), m_threshold(0.0)
+{
+}
+
+bool QQuickFrictionAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ if (!m_factor)
+ return false;
+ qreal curVX = d->curVX();
+ qreal curVY = d->curVY();
+ if (!curVX && !curVY)
+ return false;
+ qreal newVX = curVX + (curVX * m_factor * -1 * dt);
+ qreal newVY = curVY + (curVY * m_factor * -1 * dt);
+
+ if (!m_threshold) {
+ if (sign(curVX) != sign(newVX))
+ newVX = 0;
+ if (sign(curVY) != sign(newVY))
+ newVY = 0;
+ } else {
+ qreal curMag = sqrt(curVX*curVX + curVY*curVY);
+ if (curMag <= m_threshold + epsilon)
+ return false;
+ qreal newMag = sqrt(newVX*newVX + newVY*newVY);
+ if (newMag <= m_threshold + epsilon || //went past the threshold, stop there instead
+ sign(curVX) != sign(newVX) || //went so far past maybe it came out the other side!
+ sign(curVY) != sign(newVY)) {
+ qreal theta = atan2(curVY, curVX);
+ newVX = m_threshold * cos(theta);
+ newVY = m_threshold * sin(theta);
+ }
+ }
+
+ d->setInstantaneousVX(newVX);
+ d->setInstantaneousVY(newVY);
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/particles/qquickfriction_p.h b/src/particles/qquickfriction_p.h
new file mode 100644
index 0000000000..3b06710529
--- /dev/null
+++ b/src/particles/qquickfriction_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FRICTIONAFFECTOR_H
+#define FRICTIONAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickFrictionAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged)
+ Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged)
+public:
+ explicit QQuickFrictionAffector(QQuickItem *parent = 0);
+
+ qreal factor() const
+ {
+ return m_factor;
+ }
+
+ qreal threshold() const
+ {
+ return m_threshold;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+
+signals:
+
+ void factorChanged(qreal arg);
+ void thresholdChanged(qreal arg);
+
+public slots:
+
+ void setFactor(qreal arg)
+ {
+ if (m_factor != arg) {
+ m_factor = arg;
+ emit factorChanged(arg);
+ }
+ }
+
+ void setThreshold(qreal arg)
+ {
+ if (m_threshold != arg) {
+ m_threshold = arg;
+ emit thresholdChanged(arg);
+ }
+ }
+
+private:
+ qreal m_factor;
+ qreal m_threshold;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // FRICTIONAFFECTOR_H
diff --git a/src/particles/qquickgravity.cpp b/src/particles/qquickgravity.cpp
new file mode 100644
index 0000000000..cf4f35eb72
--- /dev/null
+++ b/src/particles/qquickgravity.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickgravity_p.h"
+#include <cmath>
+QT_BEGIN_NAMESPACE
+const qreal CONV = 0.017453292520444443;
+/*!
+ \qmlclass Gravity QQuickGravityAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Gravity element allows you to set an accleration in an angle
+
+ This element will accelerate all affected particles to a vector of
+ the specified magnitude in the specified angle. If the angle and acceleration do
+ not vary, it is more efficient to set the specified acceleration on the Emitter.
+
+ This element models the gravity of a massive object whose center of
+ gravity is far away (and thus the gravitational pull is effectively constant
+ across the scene). To model the gravity of an object near or inside the scene,
+ use PointAttractor.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Gravity::magnitude
+
+ Pixels per second that objects will be accelerated by.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Gravity::acceleration
+
+ Name changed to magnitude, will be removed soon.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Gravity::angle
+
+ Angle of acceleration.
+*/
+
+QQuickGravityAffector::QQuickGravityAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_magnitude(-10), m_angle(90), m_needRecalc(true)
+{
+}
+
+bool QQuickGravityAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ if (!m_magnitude)
+ return false;
+ if (m_needRecalc) {
+ m_needRecalc = false;
+ m_dx = m_magnitude * cos(m_angle * CONV);
+ m_dy = m_magnitude * sin(m_angle * CONV);
+ }
+
+ d->setInstantaneousVX(d->curVX() + m_dx*dt);
+ d->setInstantaneousVY(d->curVY() + m_dy*dt);
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/particles/qquickgravity_p.h b/src/particles/qquickgravity_p.h
new file mode 100644
index 0000000000..a738fd3ef3
--- /dev/null
+++ b/src/particles/qquickgravity_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GRAVITYAFFECTOR_H
+#define GRAVITYAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickGravityAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged)
+ Q_PROPERTY(qreal acceleration READ magnitude WRITE setAcceleration NOTIFY magnitudeChanged)
+ Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged)
+public:
+ explicit QQuickGravityAffector(QQuickItem *parent = 0);
+ qreal magnitude() const
+ {
+ return m_magnitude;
+ }
+
+ qreal angle() const
+ {
+ return m_angle;
+ }
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+signals:
+
+ void magnitudeChanged(qreal arg);
+
+ void angleChanged(qreal arg);
+
+public slots:
+void setAcceleration(qreal arg)
+{
+ qWarning() << "Gravity::acceleration has been renamed Gravity::magnitude";
+ if (m_magnitude != arg) {
+ m_magnitude = arg;
+ m_needRecalc = true;
+ emit magnitudeChanged(arg);
+ }
+}
+
+void setMagnitude(qreal arg)
+{
+ if (m_magnitude != arg) {
+ m_magnitude = arg;
+ m_needRecalc = true;
+ emit magnitudeChanged(arg);
+ }
+}
+
+void setAngle(qreal arg)
+{
+ if (m_angle != arg) {
+ m_angle = arg;
+ m_needRecalc = true;
+ emit angleChanged(arg);
+ }
+}
+
+private:
+ qreal m_magnitude;
+ qreal m_angle;
+
+ bool m_needRecalc;
+ qreal m_dx;
+ qreal m_dy;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // GRAVITYAFFECTOR_H
diff --git a/src/particles/qquickgroupgoal.cpp b/src/particles/qquickgroupgoal.cpp
new file mode 100644
index 0000000000..0d7f15a9e1
--- /dev/null
+++ b/src/particles/qquickgroupgoal.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickgroupgoal_p.h"
+#include <private/qquickspriteengine_p.h>
+#include <private/qquicksprite_p.h>
+#include "qquickimageparticle_p.h"
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass GroupGoal QQuickGroupGoalAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The GroupGoal Affector allows you to change the state of a group of a particle.
+
+*/
+/*!
+ \qmlproperty string QtQuick.Particles2::GroupGoal::goalState
+
+ The name of the group which the affected particles should move to.
+
+ Groups can have defined durations and transitions between them, setting goalState
+ will cause it to disregard any path weightings (including 0) and head down the path
+ which will reach the goalState quickest. It will pass through intermediate groups
+ on that path for their respective durations.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::GroupGoal::jump
+
+ If true, affected particles will jump directly to the target group instead of taking the
+ the shortest valid path to get there. They will also not finish their current state,
+ but immediately move to the beginning of the goal state.
+
+ Default is false.
+*/
+
+QQuickGroupGoalAffector::QQuickGroupGoalAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_jump(false)
+{
+ m_ignoresTime = true;
+}
+
+void QQuickGroupGoalAffector::setGoalState(QString arg)
+{
+ if (m_goalState != arg) {
+ m_goalState = arg;
+ emit goalStateChanged(arg);
+ }
+}
+
+bool QQuickGroupGoalAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ Q_UNUSED(dt);
+ QQuickStochasticEngine *engine = m_system->stateEngine;
+ bool notUsingEngine = false;
+ if (!engine)
+ notUsingEngine = true;
+
+ int index = d->systemIndex;
+ int goalIdx = m_system->groupIds[m_goalState];
+ if (notUsingEngine){//no stochastic states defined. So cut out the engine
+ //TODO: It's possible to move to a group that is intermediate and not used by painters or emitters - but right now that will redirect to the default group
+ m_system->moveGroups(d, goalIdx);
+ return true;
+ }else if (engine->curState(index) != goalIdx){
+ engine->setGoal(goalIdx, index, m_jump);
+ return true;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickgroupgoal_p.h b/src/particles/qquickgroupgoal_p.h
new file mode 100644
index 0000000000..f553badbd6
--- /dev/null
+++ b/src/particles/qquickgroupgoal_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GROUPGOALAFFECTOR_H
+#define GROUPGOALAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickStochasticEngine;
+
+class QQuickGroupGoalAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged)
+ Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged)
+public:
+ explicit QQuickGroupGoalAffector(QQuickItem *parent = 0);
+
+ QString goalState() const
+ {
+ return m_goalState;
+ }
+
+ bool jump() const
+ {
+ return m_jump;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+
+signals:
+
+ void goalStateChanged(QString arg);
+
+ void jumpChanged(bool arg);
+
+public slots:
+
+ void setGoalState(QString arg);
+
+ void setJump(bool arg)
+ {
+ if (m_jump != arg) {
+ m_jump = arg;
+ emit jumpChanged(arg);
+ }
+ }
+
+private:
+ QString m_goalState;
+ bool m_jump;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // GROUPGOALAFFECTOR_H
diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp
new file mode 100644
index 0000000000..d9eb6ed01b
--- /dev/null
+++ b/src/particles/qquickimageparticle.cpp
@@ -0,0 +1,1978 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qquickitem_p.h>
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgtexturematerial.h>
+#include <QtQuick/qsgtexture.h>
+#include <QFile>
+#include "qquickimageparticle_p.h"
+#include "qquickparticleemitter_p.h"
+#include <private/qquicksprite_p.h>
+#include <private/qquickspriteengine_p.h>
+#include <QOpenGLFunctions>
+#include <QtQuick/qsgengine.h>
+#include <QtQuick/private/qsgtexture_p.h>
+#include <private/qqmlglobal_p.h>
+#include <QtQml/qqmlinfo.h>
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_OPENGL_ES_2
+#define SHADER_DEFINES "#version 120\n"
+#else
+#define SHADER_DEFINES ""
+#endif
+
+//TODO: Make it larger on desktop? Requires fixing up shader code with the same define
+#define UNIFORM_ARRAY_SIZE 64
+
+static const char vertexShaderCode[] =
+ "#if defined(DEFORM)\n"
+ "attribute highp vec4 vPosTex;\n"
+ "#else\n"
+ "attribute highp vec2 vPos;\n"
+ "#endif\n"
+ "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize\n"
+ "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration\n"
+ "uniform highp float entry;\n"
+ "#if defined(COLOR)\n"
+ "attribute highp vec4 vColor;\n"
+ "#endif\n"
+ "#if defined(DEFORM)\n"
+ "attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector\n"
+ "attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate\n"
+ "#endif\n"
+ "#if defined(SPRITE)\n"
+ "attribute highp vec3 vAnimData;// w,h(premultiplied of anim), interpolation progress\n"
+ "attribute highp vec4 vAnimPos;//x,y, x,y (two frames for interpolation)\n"
+ "#endif\n"
+ "\n"
+ "uniform highp mat4 qt_Matrix;\n"
+ "uniform highp float timestamp;\n"
+ "#if defined(TABLE)\n"
+ "varying lowp vec2 tt;//y is progress if Sprite mode\n"
+ "uniform highp float sizetable[64];\n"
+ "uniform highp float opacitytable[64];\n"
+ "#endif\n"
+ "#if defined(SPRITE)\n"
+ "varying highp vec4 fTexS;\n"
+ "#elif defined(DEFORM)\n"
+ "varying highp vec2 fTex;\n"
+ "#endif\n"
+ "#if defined(COLOR)\n"
+ "varying lowp vec4 fColor;\n"
+ "#else\n"
+ "varying lowp float fFade;\n"
+ "#endif\n"
+ "\n"
+ "\n"
+ "void main() {\n"
+ "\n"
+ " highp float t = (timestamp - vData.x) / vData.y;\n"
+ " if (t < 0. || t > 1.) {\n"
+ "#if defined(DEFORM)\n"
+ " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n"
+ "#else\n"
+ " gl_PointSize = 0.;\n"
+ "#endif\n"
+ " } else {\n"
+ "#if defined(SPRITE)\n"
+ " tt.y = vAnimData.z;\n"
+ " //Calculate frame location in texture\n"
+ " fTexS.xy = vAnimPos.xy + vPosTex.zw * vAnimData.xy;\n"
+ " //Next frame is also passed, for interpolation\n"
+ " fTexS.zw = vAnimPos.zw + vPosTex.zw * vAnimData.xy;\n"
+ "\n"
+ "#elif defined(DEFORM)\n"
+ " fTex = vPosTex.zw;\n"
+ "#endif\n"
+ " highp float currentSize = mix(vData.z, vData.w, t * t);\n"
+ " lowp float fade = 1.;\n"
+ " highp float fadeIn = min(t * 10., 1.);\n"
+ " highp float fadeOut = 1. - clamp((t - 0.75) * 4.,0., 1.);\n"
+ "\n"
+ "#if defined(TABLE)\n"
+ " currentSize = currentSize * sizetable[int(floor(t*64.))];\n"
+ " fade = fade * opacitytable[int(floor(t*64.))];\n"
+ "#endif\n"
+ "\n"
+ " if (entry == 1.)\n"
+ " fade = fade * fadeIn * fadeOut;\n"
+ " else if (entry == 2.)\n"
+ " currentSize = currentSize * fadeIn * fadeOut;\n"
+ "\n"
+ " if (currentSize <= 0.) {\n"
+ "#if defined(DEFORM)\n"
+ " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n"
+ "#else\n"
+ " gl_PointSize = 0.;\n"
+ "#endif\n"
+ " } else {\n"
+ " if (currentSize < 3.)//Sizes too small look jittery as they move\n"
+ " currentSize = 3.;\n"
+ "\n"
+ " highp vec2 pos;\n"
+ "#if defined(DEFORM)\n"
+ " highp float rotation = vRotation.x + vRotation.y * t * vData.y;\n"
+ " if (vRotation.z == 1.0){\n"
+ " highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy;\n"
+ " rotation += atan(curVel.y, curVel.x);\n"
+ " }\n"
+ " highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation));\n"
+ " highp vec4 deform = vDeformVec * currentSize * (vPosTex.zzww - 0.5);\n"
+ " highp vec4 rotatedDeform = deform.xxzz * trigCalcs.xyxy;\n"
+ " rotatedDeform = rotatedDeform + (deform.yyww * trigCalcs.yxyx * vec4(-1.,1.,-1.,1.));\n"
+ " /* The readable version:\n"
+ " highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5);\n"
+ " highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5);\n"
+ " highp vec2 xRotatedDeform;\n"
+ " xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y;\n"
+ " xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y;\n"
+ " highp vec2 yRotatedDeform;\n"
+ " yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y;\n"
+ " yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y;\n"
+ " */\n"
+ " pos = vPosTex.xy\n"
+ " + rotatedDeform.xy\n"
+ " + rotatedDeform.zw\n"
+ " + vVec.xy * t * vData.y // apply speed\n"
+ " + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration\n"
+ "#else\n"
+ " pos = vPos\n"
+ " + vVec.xy * t * vData.y // apply speed vector..\n"
+ " + 0.5 * vVec.zw * pow(t * vData.y, 2.);\n"
+ " gl_PointSize = currentSize;\n"
+ "#endif\n"
+ " gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n"
+ "\n"
+ "#if defined(COLOR)\n"
+ " fColor = vColor * fade;\n"
+ "#else\n"
+ " fFade = fade;\n"
+ "#endif\n"
+ "#if defined(TABLE)\n"
+ " tt.x = t;\n"
+ "#endif\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+static const char fragmentShaderCode[] =
+ "uniform sampler2D texture;\n"
+ "uniform lowp float qt_Opacity;\n"
+ "\n"
+ "#if defined(SPRITE)\n"
+ "varying highp vec4 fTexS;\n"
+ "#elif defined(DEFORM)\n"
+ "varying highp vec2 fTex;\n"
+ "#endif\n"
+ "#if defined(COLOR)\n"
+ "varying lowp vec4 fColor;\n"
+ "#else\n"
+ "varying lowp float fFade;\n"
+ "#endif\n"
+ "#if defined(TABLE)\n"
+ "varying lowp vec2 tt;\n"
+ "uniform sampler2D colortable;\n"
+ "#endif\n"
+ "\n"
+ "void main() {\n"
+ "#if defined(SPRITE)\n"
+ " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), tt.y)\n"
+ " * fColor\n"
+ " * texture2D(colortable, tt)\n"
+ " * qt_Opacity;\n"
+ "#elif defined(TABLE)\n"
+ " gl_FragColor = texture2D(texture, fTex)\n"
+ " * fColor\n"
+ " * texture2D(colortable, tt)\n"
+ " * qt_Opacity;\n"
+ "#elif defined(DEFORM)\n"
+ " gl_FragColor = (texture2D(texture, fTex)) * fColor * qt_Opacity;\n"
+ "#elif defined(COLOR)\n"
+ " gl_FragColor = (texture2D(texture, gl_PointCoord)) * fColor * qt_Opacity;\n"
+ "#else\n"
+ " gl_FragColor = texture2D(texture, gl_PointCoord) * (fFade * qt_Opacity);\n"
+ "#endif\n"
+ "}\n";
+
+const qreal CONV = 0.017453292519943295;
+class ImageMaterialData
+{
+ public:
+ ImageMaterialData()
+ : texture(0), colorTable(0)
+ {}
+
+ ~ImageMaterialData(){
+ delete texture;
+ delete colorTable;
+ }
+
+ QSGTexture *texture;
+ QSGTexture *colorTable;
+ float sizeTable[UNIFORM_ARRAY_SIZE];
+ float opacityTable[UNIFORM_ARRAY_SIZE];
+
+ qreal timestamp;
+ qreal entry;
+ QSizeF animSheetSize;
+};
+
+class TabledMaterialData : public ImageMaterialData {};
+class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData)
+
+public:
+ TabledMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
+ << "vColor" << "vDeformVec" << "vRotation";
+ };
+
+ void initialize() {
+ QSGSimpleMaterialShader<TabledMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ program()->setUniformValue("colortable", 1);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ m_sizetable_id = program()->uniformLocation("sizetable");
+ m_opacitytable_id = program()->uniformLocation("opacitytable");
+ }
+
+ void updateState(const TabledMaterialData* d, const TabledMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE1);
+ d->colorTable->bind();
+
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1);
+ program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
+ }
+
+ int m_entry_id;
+ int m_timestamp_id;
+ int m_sizetable_id;
+ int m_opacitytable_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+class DeformableMaterialData : public ImageMaterialData {};
+class DeformableMaterial : public QSGSimpleMaterialShader<DeformableMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData)
+
+public:
+ DeformableMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define DEFORM\n#define COLOR\n")
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define DEFORM\n#define COLOR\n")
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
+ << "vColor" << "vDeformVec" << "vRotation";
+ };
+
+ void initialize() {
+ QSGSimpleMaterialShader<DeformableMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ }
+
+ void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ }
+
+ int m_entry_id;
+ int m_timestamp_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+class SpriteMaterialData : public ImageMaterialData {};
+class SpriteMaterial : public QSGSimpleMaterialShader<SpriteMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData)
+
+public:
+ SpriteMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
+ << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
+ };
+
+ void initialize() {
+ QSGSimpleMaterialShader<SpriteMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ program()->setUniformValue("colortable", 1);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ //Don't actually expose the animSheetSize in the shader, it's currently only used for CPU calculations.
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ m_sizetable_id = program()->uniformLocation("sizetable");
+ m_opacitytable_id = program()->uniformLocation("opacitytable");
+ }
+
+ void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE1);
+ d->colorTable->bind();
+
+ // make sure we end by setting GL_TEXTURE0 as active texture
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, 64, 1);
+ program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
+ }
+
+ int m_timestamp_id;
+ int m_entry_id;
+ int m_sizetable_id;
+ int m_opacitytable_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+class ColoredMaterialData : public ImageMaterialData {};
+class ColoredMaterial : public QSGSimpleMaterialShader<ColoredMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData)
+
+public:
+ ColoredMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define COLOR\n")
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + QByteArray("#define COLOR\n")
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ void activate() {
+ QSGSimpleMaterialShader<ColoredMaterialData>::activate();
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
+ glEnable(GL_POINT_SPRITE);
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+#endif
+ }
+
+ void deactivate() {
+ QSGSimpleMaterialShader<ColoredMaterialData>::deactivate();
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
+ glDisable(GL_POINT_SPRITE);
+ glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
+#endif
+ }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor";
+ }
+
+ void initialize() {
+ QSGSimpleMaterialShader<ColoredMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ }
+
+ void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ }
+
+ int m_timestamp_id;
+ int m_entry_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+class SimpleMaterialData : public ImageMaterialData {};
+class SimpleMaterial : public QSGSimpleMaterialShader<SimpleMaterialData>
+{
+ QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData)
+
+public:
+ SimpleMaterial()
+ {
+ m_vertex_code = QByteArray(SHADER_DEFINES)
+ + vertexShaderCode;
+
+ m_fragment_code = QByteArray(SHADER_DEFINES)
+ + fragmentShaderCode;
+
+ Q_ASSERT(!m_vertex_code.isNull());
+ Q_ASSERT(!m_fragment_code.isNull());
+ }
+
+ const char *vertexShader() const { return m_vertex_code.constData(); }
+ const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+ void activate() {
+ QSGSimpleMaterialShader<SimpleMaterialData>::activate();
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
+ glEnable(GL_POINT_SPRITE);
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+#endif
+ }
+
+ void deactivate() {
+ QSGSimpleMaterialShader<SimpleMaterialData>::deactivate();
+#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
+ glDisable(GL_POINT_SPRITE);
+ glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
+#endif
+ }
+
+ QList<QByteArray> attributes() const {
+ return QList<QByteArray>() << "vPos" << "vData" << "vVec";
+ }
+
+ void initialize() {
+ QSGSimpleMaterialShader<SimpleMaterialData>::initialize();
+ program()->bind();
+ program()->setUniformValue("texture", 0);
+ glFuncs = QOpenGLContext::currentContext()->functions();
+ m_timestamp_id = program()->uniformLocation("timestamp");
+ m_entry_id = program()->uniformLocation("entry");
+ }
+
+ void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) {
+ glFuncs->glActiveTexture(GL_TEXTURE0);
+ d->texture->bind();
+
+ program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+ program()->setUniformValue(m_entry_id, (float) d->entry);
+ }
+
+ int m_timestamp_id;
+ int m_entry_id;
+ QByteArray m_vertex_code;
+ QByteArray m_fragment_code;
+ QOpenGLFunctions* glFuncs;
+};
+
+void fillUniformArrayFromImage(float* array, const QImage& img, int size)
+{
+ if (img.isNull()){
+ for (int i=0; i<size; i++)
+ array[i] = 1.0;
+ return;
+ }
+ QImage scaled = img.scaled(size,1);
+ for (int i=0; i<size; i++)
+ array[i] = qAlpha(scaled.pixel(i,0))/255.0;
+}
+
+/*!
+ \qmlclass ImageParticle QQuickImageParticle
+ \inqmlmodule QtQuick.Particles 2
+ \inherits ParticlePainter
+ \brief The ImageParticle element visualizes logical particles using an image
+
+ This element renders a logical particle as an image. The image can be
+ \list
+ \li colorized
+ \li rotated
+ \li deformed
+ \li a sprite-based animation
+ \endlist
+
+ ImageParticles implictly share data on particles if multiple ImageParticles are painting
+ the same logical particle group. This is broken down along the four capabilities listed
+ above. So if one ImageParticle defines data for rendering the particles in one of those
+ capabilities, and the other does not, then both will draw the particles the same in that
+ aspect automatically. This is primarily useful when there is some random variation on
+ the particle which is supposed to stay with it when switching painters. If both ImageParticles
+ define how they should appear for that aspect, they diverge and each appears as it is defined.
+
+ This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly
+ set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole.
+ So if you explicity set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation
+ to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from
+ other ImageParticles.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::ImageParticle::source
+
+ The source image to be used.
+
+ If the image is a sprite animation, use the sprite property instead.
+*/
+/*!
+ \qmlproperty list<Sprite> QtQuick.Particles2::ImageParticle::sprites
+
+ The sprite or sprites used to draw this particle.
+
+ Note that the sprite image will be scaled to a square based on the size of
+ the particle being rendered.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::ImageParticle::colorTable
+
+ An image whose color will be used as a 1D texture to determine color over life. E.g. when
+ the particle is halfway through its lifetime, it will have the color specified halfway
+ across the image.
+
+ This color is blended with the color property and the color of the source image.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable
+
+ An image whose opacity will be used as a 1D texture to determine size over life.
+
+ This property is expected to be removed shortly, in favor of custom easing curves to determine size over life.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable
+
+ An image whose opacity will be used as a 1D texture to determine size over life.
+
+ This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life.
+*/
+/*!
+ \qmlproperty color QtQuick.Particles2::ImageParticle::color
+
+ If a color is specified, the provided image will be colorized with it.
+
+ Default is white (no change).
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::colorVariation
+
+ This number represents the color variation applied to individual particles.
+ Setting colorVariation is the same as setting redVariation, greenVariation,
+ and blueVariation to the same number.
+
+ Each channel can vary between particle by up to colorVariation from its usual color.
+
+ Color is measured, per channel, from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::redVariation
+ The variation in the red color channel between particles.
+
+ Color is measured, per channel, from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::greenVariation
+ The variation in the green color channel between particles.
+
+ Color is measured, per channel, from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::blueVariation
+ The variation in the blue color channel between particles.
+
+ Color is measured, per channel, from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::alpha
+ An alpha to be applied to the image. This value is multiplied by the value in
+ the image, and the value in the color property.
+
+ Particles have additive blending, so lower alpha on single particles leads
+ to stronger effects when multiple particles overlap.
+
+ Alpha is measured from 0.0 to 1.0.
+
+ Default is 1.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::alphaVariation
+ The variation in the alpha channel between particles.
+
+ Alpha is measured from 0.0 to 1.0.
+
+ Default is 0.0
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::rotation
+
+ If set the image will be rotated by this many degrees before it is drawn.
+
+ The particle coordinates are not transformed.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::rotationVariation
+
+ If set the rotation of individual particles will vary by up to this much
+ between particles.
+
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeed
+
+ If set particles will rotate at this speed in degrees/second.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeedVariation
+
+ If set the rotationSpeed of individual particles will vary by up to this much
+ between particles.
+
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::ImageParticle::autoRotation
+
+ If set to true then a rotation will be applied on top of the particles rotation, so
+ that it faces the direction of travel. So to face away from the direction of travel,
+ set autoRotation to true and rotation to 180.
+
+ Default is false
+*/
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::xVector
+
+ Allows you to deform the particle image when drawn. The rectangular image will
+ be deformed so that the horizontal sides are in the shape of this vector instead
+ of (1,0).
+*/
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::yVector
+
+ Allows you to deform the particle image when drawn. The rectangular image will
+ be deformed so that the vertical sides are in the shape of this vector instead
+ of (0,1).
+*/
+/*!
+ \qmlproperty EntryEffect QtQuick.Particles2::ImageParticle::entryEffect
+
+ This property provides basic and cheap entrance and exit effects for the particles.
+ For fine-grained control, see sizeTable and opacityTable.
+
+ Acceptable values are
+ \list
+ \li ImageParticle.None: Particles just appear and disappear.
+ \li ImageParticle.Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
+ \li ImageParticle.Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
+ \endlist
+
+ Default value is Fade.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::ImageParticle::spritesInterpolate
+
+ If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
+ the sprites look smoother.
+
+ Default is true.
+*/
+
+/*!
+ \qmlproperty Status QtQuick.Particles2::ImageParticle::status
+
+ The status of loading the image.
+*/
+
+
+QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
+ : QQuickParticlePainter(parent)
+ , m_image(0)
+ , m_colorTable(0)
+ , m_sizeTable(0)
+ , m_opacityTable(0)
+ , m_color_variation(0.0)
+ , m_rootNode(0)
+ , m_material(0)
+ , m_alphaVariation(0.0)
+ , m_alpha(1.0)
+ , m_redVariation(0.0)
+ , m_greenVariation(0.0)
+ , m_blueVariation(0.0)
+ , m_rotation(0)
+ , m_rotationVariation(0)
+ , m_rotationSpeed(0)
+ , m_rotationSpeedVariation(0)
+ , m_autoRotation(false)
+ , m_xVector(0)
+ , m_yVector(0)
+ , m_spriteEngine(0)
+ , m_spritesInterpolate(true)
+ , m_explicitColor(false)
+ , m_explicitRotation(false)
+ , m_explicitDeformation(false)
+ , m_explicitAnimation(false)
+ , m_bypassOptimizations(false)
+ , perfLevel(Unknown)
+ , m_lastLevel(Unknown)
+ , m_debugMode(false)
+ , m_entryEffect(Fade)
+ , m_buildingNodes(false)
+{
+ setFlag(ItemHasContents);
+}
+
+QQuickImageParticle::~QQuickImageParticle()
+{
+}
+
+QQmlListProperty<QQuickSprite> QQuickImageParticle::sprites()
+{
+ return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+}
+
+void QQuickImageParticle::sceneGraphInvalidated()
+{
+ m_nodes.clear();
+ m_rootNode = 0;
+ m_material = 0;
+}
+
+void QQuickImageParticle::setImage(const QUrl &image)
+{
+ if (image.isEmpty()){
+ if (m_image) {
+ delete m_image;
+ emit imageChanged();
+ }
+ return;
+ }
+
+ if (!m_image)
+ m_image = new ImageData;
+ if (image == m_image->source)
+ return;
+ m_image->source = image;
+ emit imageChanged();
+ reset();
+}
+
+
+void QQuickImageParticle::setColortable(const QUrl &table)
+{
+ if (table.isEmpty()){
+ if (m_colorTable) {
+ delete m_colorTable;
+ emit colortableChanged();
+ }
+ return;
+ }
+
+ if (!m_colorTable)
+ m_colorTable = new ImageData;
+ if (table == m_colorTable->source)
+ return;
+ m_colorTable->source = table;
+ emit colortableChanged();
+ reset();
+}
+
+void QQuickImageParticle::setSizetable(const QUrl &table)
+{
+ if (table.isEmpty()){
+ if (m_sizeTable) {
+ delete m_sizeTable;
+ emit sizetableChanged();
+ }
+ return;
+ }
+
+ if (!m_sizeTable)
+ m_sizeTable = new ImageData;
+ if (table == m_sizeTable->source)
+ return;
+ m_sizeTable->source = table;
+ emit sizetableChanged();
+ reset();
+}
+
+void QQuickImageParticle::setOpacitytable(const QUrl &table)
+{
+ if (table.isEmpty()){
+ if (m_opacityTable) {
+ delete m_opacityTable;
+ emit opacitytableChanged();
+ }
+ return;
+ }
+
+ if (!m_opacityTable)
+ m_opacityTable = new ImageData;
+ if (table == m_opacityTable->source)
+ return;
+ m_opacityTable->source = table;
+ emit opacitytableChanged();
+ reset();
+}
+
+void QQuickImageParticle::setColor(const QColor &color)
+{
+ if (color == m_color)
+ return;
+ m_color = color;
+ emit colorChanged();
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setColorVariation(qreal var)
+{
+ if (var == m_color_variation)
+ return;
+ m_color_variation = var;
+ emit colorVariationChanged();
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setAlphaVariation(qreal arg)
+{
+ if (m_alphaVariation != arg) {
+ m_alphaVariation = arg;
+ emit alphaVariationChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setAlpha(qreal arg)
+{
+ if (m_alpha != arg) {
+ m_alpha = arg;
+ emit alphaChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setRedVariation(qreal arg)
+{
+ if (m_redVariation != arg) {
+ m_redVariation = arg;
+ emit redVariationChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setGreenVariation(qreal arg)
+{
+ if (m_greenVariation != arg) {
+ m_greenVariation = arg;
+ emit greenVariationChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setBlueVariation(qreal arg)
+{
+ if (m_blueVariation != arg) {
+ m_blueVariation = arg;
+ emit blueVariationChanged(arg);
+ }
+ m_explicitColor = true;
+ if (perfLevel < Colored)
+ reset();
+}
+
+void QQuickImageParticle::setRotation(qreal arg)
+{
+ if (m_rotation != arg) {
+ m_rotation = arg;
+ emit rotationChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setRotationVariation(qreal arg)
+{
+ if (m_rotationVariation != arg) {
+ m_rotationVariation = arg;
+ emit rotationVariationChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setRotationSpeed(qreal arg)
+{
+ if (m_rotationSpeed != arg) {
+ m_rotationSpeed = arg;
+ emit rotationSpeedChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setRotationSpeedVariation(qreal arg)
+{
+ if (m_rotationSpeedVariation != arg) {
+ m_rotationSpeedVariation = arg;
+ emit rotationSpeedVariationChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setAutoRotation(bool arg)
+{
+ if (m_autoRotation != arg) {
+ m_autoRotation = arg;
+ emit autoRotationChanged(arg);
+ }
+ m_explicitRotation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setXVector(QQuickDirection* arg)
+{
+ if (m_xVector != arg) {
+ m_xVector = arg;
+ emit xVectorChanged(arg);
+ }
+ m_explicitDeformation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setYVector(QQuickDirection* arg)
+{
+ if (m_yVector != arg) {
+ m_yVector = arg;
+ emit yVectorChanged(arg);
+ }
+ m_explicitDeformation = true;
+ if (perfLevel < Deformable)
+ reset();
+}
+
+void QQuickImageParticle::setSpritesInterpolate(bool arg)
+{
+ if (m_spritesInterpolate != arg) {
+ m_spritesInterpolate = arg;
+ emit spritesInterpolateChanged(arg);
+ }
+}
+
+void QQuickImageParticle::setBypassOptimizations(bool arg)
+{
+ if (m_bypassOptimizations != arg) {
+ m_bypassOptimizations = arg;
+ emit bypassOptimizationsChanged(arg);
+ }
+ if (perfLevel < 9999)
+ reset();
+}
+
+void QQuickImageParticle::setEntryEffect(EntryEffect arg)
+{
+ if (m_entryEffect != arg) {
+ m_entryEffect = arg;
+ if (m_material)
+ getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
+ emit entryEffectChanged(arg);
+ }
+}
+
+void QQuickImageParticle::resetColor()
+{
+ m_explicitColor = false;
+ foreach (const QString &str, m_groups)
+ foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
+ if (d->colorOwner == this)
+ d->colorOwner = 0;
+ m_color = QColor();
+ m_color_variation = 0.0f;
+ m_redVariation = 0.0f;
+ m_blueVariation = 0.0f;
+ m_greenVariation = 0.0f;
+ m_alpha = 1.0f;
+ m_alphaVariation = 0.0f;
+}
+
+void QQuickImageParticle::resetRotation()
+{
+ m_explicitRotation = false;
+ foreach (const QString &str, m_groups)
+ foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
+ if (d->rotationOwner == this)
+ d->rotationOwner = 0;
+ m_rotation = 0;
+ m_rotationVariation = 0;
+ m_rotationSpeed = 0;
+ m_rotationSpeedVariation = 0;
+ m_autoRotation = false;
+}
+
+void QQuickImageParticle::resetDeformation()
+{
+ m_explicitDeformation = false;
+ foreach (const QString &str, m_groups)
+ foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
+ if (d->deformationOwner == this)
+ d->deformationOwner = 0;
+ if (m_xVector)
+ delete m_xVector;
+ if (m_yVector)
+ delete m_yVector;
+ m_xVector = 0;
+ m_yVector = 0;
+}
+
+void QQuickImageParticle::reset()
+{
+ QQuickParticlePainter::reset();
+ m_pleaseReset = true;
+ update();
+}
+
+void QQuickImageParticle::createEngine()
+{
+ if (m_spriteEngine)
+ delete m_spriteEngine;
+ if (m_sprites.count()) {
+ m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
+ connect(m_spriteEngine, SIGNAL(stateChanged(int)),
+ this, SLOT(spriteAdvance(int)), Qt::DirectConnection);
+ m_explicitAnimation = true;
+ } else {
+ m_spriteEngine = 0;
+ m_explicitAnimation = false;
+ }
+ reset();
+}
+
+static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
+ QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT) // Vectors
+};
+
+static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
+{
+ 3, // Attribute Count
+ ( 2 + 4 + 4 ) * sizeof(float),
+ SimpleParticle_Attributes
+};
+
+static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
+ QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
+ QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
+};
+
+static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
+{
+ 4, // Attribute Count
+ ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
+ ColoredParticle_Attributes
+};
+
+static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
+ QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
+ QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
+ QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
+ QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
+};
+
+static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
+{
+ 6, // Attribute Count
+ (4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
+ DeformableParticle_Attributes
+};
+
+static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
+ QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
+ QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
+ QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
+ QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
+ QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
+ QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Anim Data
+ QSGGeometry::Attribute::create(7, 4, GL_FLOAT) // Anim Pos
+};
+
+static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
+{
+ 8, // Attribute Count
+ (4 + 4 + 4 + 4 + 3 + 3 + 4) * sizeof(float) + 4 * sizeof(uchar),
+ SpriteParticle_Attributes
+};
+
+void QQuickImageParticle::clearShadows()
+{
+ foreach (const QVector<QQuickParticleData*> data, m_shadowData)
+ qDeleteAll(data);
+ m_shadowData.clear();
+}
+
+//Only call if you need to, may initialize the whole array first time
+QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
+{
+ //Will return datum if the datum is a sentinel or uninitialized, to centralize that one check
+ if (datum->systemIndex == -1)
+ return datum;
+ QQuickParticleGroupData* gd = m_system->groupData[datum->group];
+ if (!m_shadowData.contains(datum->group)) {
+ QVector<QQuickParticleData*> data;
+ for (int i=0; i<gd->size(); i++){
+ QQuickParticleData* datum = new QQuickParticleData(m_system);
+ *datum = *(gd->data[i]);
+ data << datum;
+ }
+ m_shadowData.insert(datum->group, data);
+ }
+ //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
+
+ return m_shadowData[datum->group][datum->index];
+}
+
+bool QQuickImageParticle::loadingSomething()
+{
+ return (m_image && m_image->pix.isLoading())
+ || (m_colorTable && m_colorTable->pix.isLoading())
+ || (m_sizeTable && m_sizeTable->pix.isLoading())
+ || (m_opacityTable && m_opacityTable->pix.isLoading())
+ || (m_spriteEngine && m_spriteEngine->isLoading());
+}
+
+void QQuickImageParticle::buildParticleNodes()//Starts async parts, like loading images.
+{
+ if (m_rootNode || loadingSomething())
+ return;
+
+ if (!m_buildingNodes) {
+ if (m_image) {//ImageData created on setSource
+ m_image->pix.clear(this);
+ m_image->pix.load(qmlEngine(this), m_image->source);
+ }
+
+ if (m_spriteEngine)
+ m_spriteEngine->startAssemblingImage();
+
+ if (m_colorTable)
+ m_colorTable->pix.load(qmlEngine(this), m_colorTable->source);
+
+ if (m_sizeTable)
+ m_sizeTable->pix.load(qmlEngine(this), m_sizeTable->source);
+
+ if (m_opacityTable)
+ m_opacityTable->pix.load(qmlEngine(this), m_opacityTable->source);
+
+ m_buildingNodes = true;
+ if (loadingSomething())
+ return;
+ }
+ finishBuildParticleNodes();
+}
+
+void QQuickImageParticle::finishBuildParticleNodes()
+{
+ m_buildingNodes = false;
+#ifdef QT_OPENGL_ES_2
+ if (m_count * 4 > 0xffff) {
+ printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
+ return;
+ }
+#endif
+
+ if (count() <= 0)
+ return;
+
+ m_debugMode = m_system->m_debugMode;
+
+ if (m_sprites.count() || m_bypassOptimizations) {
+ perfLevel = Sprites;
+ } else if (m_colorTable || m_sizeTable || m_opacityTable) {
+ perfLevel = Tabled;
+ } else if (m_autoRotation || m_rotation || m_rotationVariation
+ || m_rotationSpeed || m_rotationSpeedVariation
+ || m_xVector || m_yVector) {
+ perfLevel = Deformable;
+ } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
+ || m_redVariation || m_blueVariation || m_greenVariation) {
+ perfLevel = Colored;
+ } else {
+ perfLevel = Simple;
+ }
+
+ foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders
+ int gIdx = m_system->groupIds[str];
+ foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){
+ QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
+ if (other){
+ if (other->perfLevel > perfLevel) {
+ if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
+ if (perfLevel < Deformable)
+ perfLevel = Deformable;
+ } else {
+ perfLevel = other->perfLevel;
+ }
+ } else if (other->perfLevel < perfLevel) {
+ other->reset();
+ }
+ }
+ }
+ }
+#ifdef Q_OS_WIN
+ if (perfLevel < Deformable) //QTBUG-24540 , point sprite 'extension' isn't working on windows.
+ perfLevel = Deformable;
+#endif
+
+ if (perfLevel >= Colored && !m_color.isValid())
+ m_color = QColor(Qt::white);//Hidden default, but different from unset
+
+ clearShadows();
+ if (m_material)
+ m_material = 0;
+
+ //Setup material
+ QImage colortable;
+ QImage sizetable;
+ QImage opacitytable;
+ QImage image;
+ bool imageLoaded = false;
+ switch (perfLevel) {//Fallthrough intended
+ case Sprites:
+ if (!m_spriteEngine) {
+ qWarning() << "ImageParticle: No sprite engine...";
+ //Sprite performance mode with static image is supported, but not advised
+ //Note that in this case it always uses shadow data
+ } else {
+ image = m_spriteEngine->assembledImage();
+ if (image.isNull())//Warning is printed in engine
+ return;
+ imageLoaded = true;
+ }
+ m_material = SpriteMaterial::createMaterial();
+ if (imageLoaded)
+ getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
+ getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
+ if (m_spriteEngine)
+ m_spriteEngine->setCount(m_count);
+ case Tabled:
+ if (!m_material)
+ m_material = TabledMaterial::createMaterial();
+
+ if (m_colorTable) {
+ if (m_colorTable->pix.isReady())
+ colortable = m_colorTable->pix.image();
+ else
+ qmlInfo(this) << "Error loading color table: " << m_colorTable->pix.error();
+ }
+
+ if (m_sizeTable) {
+ if (m_sizeTable->pix.isReady())
+ sizetable = m_sizeTable->pix.image();
+ else
+ qmlInfo(this) << "Error loading size table: " << m_sizeTable->pix.error();
+ }
+
+ if (m_opacityTable) {
+ if (m_opacityTable->pix.isReady())
+ opacitytable = m_opacityTable->pix.image();
+ else
+ qmlInfo(this) << "Error loading opacity table: " << m_opacityTable->pix.error();
+ }
+
+ if (colortable.isNull()){//###Goes through image just for this
+ colortable = QImage(1,1,QImage::Format_ARGB32_Premultiplied);
+ colortable.fill(Qt::white);
+ }
+ getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
+ fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
+ fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
+ case Deformable:
+ if (!m_material)
+ m_material = DeformableMaterial::createMaterial();
+ case Colored:
+ if (!m_material)
+ m_material = ColoredMaterial::createMaterial();
+ default://Also Simple
+ if (!m_material)
+ m_material = SimpleMaterial::createMaterial();
+ if (!imageLoaded) {
+ if (!m_image->pix.isReady()) {
+ qmlInfo(this) << m_image->pix.error();
+ delete m_material;
+ return;
+ }
+ //getState<ImageMaterialData>(m_material)->texture //TODO: Shouldn't this be better? But not crash?
+ // = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
+ getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image());
+ }
+ getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
+ getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
+ m_material->setFlag(QSGMaterial::Blending);
+ }
+
+ m_nodes.clear();
+ foreach (const QString &str, m_groups){
+ int gIdx = m_system->groupIds[str];
+ int count = m_system->groupData[gIdx]->size();
+ QSGGeometryNode* node = new QSGGeometryNode();
+ node->setMaterial(m_material);
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ m_nodes.insert(gIdx, node);
+ m_idxStarts.insert(gIdx, m_lastIdxStart);
+ m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx));
+ m_lastIdxStart += count;
+
+ //Create Particle Geometry
+ int vCount = count * 4;
+ int iCount = count * 6;
+
+ QSGGeometry *g;
+ if (perfLevel == Sprites)
+ g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
+ else if (perfLevel == Tabled)
+ g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
+ else if (perfLevel == Deformable)
+ g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
+ else if (perfLevel == Colored)
+ g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0);
+ else //Simple
+ g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0);
+
+ node->setGeometry(g);
+ if (perfLevel <= Colored){
+ g->setDrawingMode(GL_POINTS);
+ if (m_debugMode){
+ GLfloat pointSizeRange[2];
+ glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
+ qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
+ }
+ }else
+ g->setDrawingMode(GL_TRIANGLES);
+
+ for (int p=0; p < count; ++p)
+ commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch
+
+ if (perfLevel == Sprites)
+ initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
+ else if (perfLevel == Tabled)
+ initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
+ else if (perfLevel == Deformable)
+ initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
+
+ if (perfLevel > Colored){
+ quint16 *indices = g->indexDataAsUShort();
+ for (int i=0; i < count; ++i) {
+ int o = i * 4;
+ indices[0] = o;
+ indices[1] = o + 1;
+ indices[2] = o + 2;
+ indices[3] = o + 1;
+ indices[4] = o + 3;
+ indices[5] = o + 2;
+ indices += 6;
+ }
+ }
+ }
+
+ if (perfLevel == Sprites)
+ spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
+
+ foreach (QSGGeometryNode* node, m_nodes){
+ if (node == *(m_nodes.begin()))
+ node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
+ else
+ (*(m_nodes.begin()))->appendChildNode(node);
+ }
+
+ m_rootNode = *(m_nodes.begin());
+ update();
+}
+
+QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+{
+ if (m_pleaseReset){
+ m_lastLevel = perfLevel;
+
+ delete m_rootNode;//Automatically deletes children, and SG manages material lifetime
+ m_rootNode = 0;
+ m_nodes.clear();
+
+ m_idxStarts.clear();
+ m_startsIdx.clear();
+ m_lastIdxStart = 0;
+
+ m_material = 0;
+
+ m_pleaseReset = false;
+ m_buildingNodes = false;//Cancel a part-way build
+ }
+
+ if (m_system && m_system->isRunning() && !m_system->isPaused()){
+ prepareNextFrame();
+ if (m_rootNode) {
+ update();
+ foreach (QSGGeometryNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyGeometry);
+ } else if (m_buildingNodes) {
+ update();//To call prepareNextFrame() again from the renderThread
+ }
+ }
+
+ return m_rootNode;
+}
+
+void QQuickImageParticle::prepareNextFrame()
+{
+ if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
+ buildParticleNodes();
+ if (m_debugMode) {
+ qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
+ qDebug() << "QQuickImageParticle Nodes: ";
+ int count = 0;
+ foreach (int i, m_nodes.keys()) {
+ qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)";
+ count += m_system->groupData[i]->size();
+ }
+ qDebug() << "Total count: " << count;
+ }
+ if (m_rootNode == 0)
+ return;
+ }
+ qint64 timeStamp = m_system->systemSync(this);
+
+ qreal time = timeStamp / 1000.;
+
+ switch (perfLevel){//Fall-through intended
+ case Sprites:
+ //Advance State
+ if (m_spriteEngine)
+ m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
+ spritesUpdate(time);
+ case Tabled:
+ case Deformable:
+ case Colored:
+ case Simple:
+ default: //Also Simple
+ getState<ImageMaterialData>(m_material)->timestamp = time;
+ break;
+ }
+ foreach (QSGGeometryNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
+}
+
+void QQuickImageParticle::spritesUpdate(qreal time)
+{
+ // Sprite progression handled CPU side, so as to have per-frame control.
+ foreach (const QString &str, m_groups) {
+ int gIdx = m_system->groupIds[str];
+ foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) {
+ QSGGeometryNode *node = m_nodes[gIdx];
+ if (!node)
+ continue;
+ //TODO: Interpolate between two different animations if it's going to transition next frame
+ // This is particularly important for cut-up sprites.
+ QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
+ int spriteIdx = 0;
+ for (int i = 0; i<m_startsIdx.count(); i++) {
+ if (m_startsIdx[i].second == gIdx){
+ spriteIdx = m_startsIdx[i].first + datum->index;
+ break;
+ }
+ }
+
+ double frameAt;
+ qreal progress = 0;
+
+ if (datum->frameDuration > 0) {
+ qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
+ frame = qBound((qreal)0.0, frame, (qreal)((qreal)datum->frameCount - 1.0));//Stop at count-1 frames until we have between anim interpolation
+ if (m_spritesInterpolate)
+ progress = modf(frame,&frameAt);
+ else
+ modf(frame,&frameAt);
+ } else {
+ datum->frameAt++;
+ if (datum->frameAt >= datum->frameCount){
+ datum->frameAt = 0;
+ m_spriteEngine->advance(spriteIdx);
+ }
+ frameAt = datum->frameAt;
+ }
+ if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too?
+ frameAt = (datum->frameCount - 1) - frameAt;
+ QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize;
+ qreal y = datum->animY / sheetSize.height();
+ qreal w = datum->animWidth / sheetSize.width();
+ qreal h = datum->animHeight / sheetSize.height();
+ qreal x1 = datum->animX / sheetSize.width();
+ x1 += frameAt * w;
+ qreal x2 = x1;
+ if (frameAt < (datum->frameCount-1))
+ x2 += w;
+
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
+ spriteVertices += datum->index*4;
+ for (int i=0; i<4; i++) {
+ spriteVertices[i].animX1 = x1;
+ spriteVertices[i].animY1 = y;
+ spriteVertices[i].animX2 = x2;
+ spriteVertices[i].animY2 = y;
+ spriteVertices[i].animW = w;
+ spriteVertices[i].animH = h;
+ spriteVertices[i].animProgress = progress;
+ }
+ node->setFlag(QSGNode::OwnsGeometry, true);
+ }
+ }
+}
+
+void QQuickImageParticle::spriteAdvance(int spriteIdx)
+{
+ if (!m_startsIdx.count())//Probably overly defensive
+ return;
+
+ int gIdx = -1;
+ int i;
+ for (i = 0; i<m_startsIdx.count(); i++) {
+ if (spriteIdx < m_startsIdx[i].first) {
+ gIdx = m_startsIdx[i-1].second;
+ break;
+ }
+ }
+ if (gIdx == -1)
+ gIdx = m_startsIdx[i-1].second;
+ int pIdx = spriteIdx - m_startsIdx[i-1].first;
+
+ QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
+ QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
+
+ datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
+ datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
+ datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
+ datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / datum->frameCount;
+ datum->animX = m_spriteEngine->spriteX(spriteIdx);
+ datum->animY = m_spriteEngine->spriteY(spriteIdx);
+ datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
+ datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
+}
+
+void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d)
+{
+ d->color = c;
+ //TODO: get index for reload - or make function take an index
+}
+
+void QQuickImageParticle::initialize(int gIdx, int pIdx)
+{
+ Color4ub color;
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ qreal redVariation = m_color_variation + m_redVariation;
+ qreal greenVariation = m_color_variation + m_greenVariation;
+ qreal blueVariation = m_color_variation + m_blueVariation;
+ int spriteIdx = 0;
+ if (m_spriteEngine) {
+ spriteIdx = m_idxStarts[gIdx] + datum->index;
+ if (spriteIdx >= m_spriteEngine->count())
+ m_spriteEngine->setCount(spriteIdx+1);
+ }
+
+ float rotation;
+ float rotationSpeed;
+ float autoRotate;
+ switch (perfLevel){//Fall-through is intended on all of them
+ case Sprites:
+ // Initial Sprite State
+ if (m_explicitAnimation && m_spriteEngine){
+ if (!datum->animationOwner)
+ datum->animationOwner = this;
+ QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
+ writeTo->animT = writeTo->t;
+ //writeTo->animInterpolate = m_spritesInterpolate;
+ if (m_spriteEngine){
+ m_spriteEngine->start(spriteIdx);
+ writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
+ writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / writeTo->frameCount;
+ writeTo->animIdx = 0;//Always starts at 0
+ writeTo->frameAt = -1;
+ writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
+ writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
+ writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
+ writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
+ }
+ } else {
+ QQuickParticleData* writeTo = getShadowDatum(datum);
+ writeTo->animT = datum->t;
+ writeTo->frameCount = 1;
+ writeTo->frameDuration = 60000000.0;
+ writeTo->frameAt = -1;
+ writeTo->animIdx = 0;
+ writeTo->animT = 0;
+ writeTo->animX = writeTo->animY = 0;
+ writeTo->animWidth = getState<ImageMaterialData>(m_material)->animSheetSize.width();
+ writeTo->animHeight = getState<ImageMaterialData>(m_material)->animSheetSize.height();
+ }
+ case Tabled:
+ case Deformable:
+ //Initial Rotation
+ if (m_explicitDeformation){
+ if (!datum->deformationOwner)
+ datum->deformationOwner = this;
+ if (m_xVector){
+ const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
+ if (datum->deformationOwner == this) {
+ datum->xx = ret.x();
+ datum->xy = ret.y();
+ } else {
+ getShadowDatum(datum)->xx = ret.x();
+ getShadowDatum(datum)->xy = ret.y();
+ }
+ }
+ if (m_yVector){
+ const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
+ if (datum->deformationOwner == this) {
+ datum->yx = ret.x();
+ datum->yy = ret.y();
+ } else {
+ getShadowDatum(datum)->yx = ret.x();
+ getShadowDatum(datum)->yy = ret.y();
+ }
+ }
+ }
+
+ if (m_explicitRotation){
+ if (!datum->rotationOwner)
+ datum->rotationOwner = this;
+ rotation =
+ (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
+ rotationSpeed =
+ (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
+ autoRotate = m_autoRotation?1.0:0.0;
+ if (datum->rotationOwner == this) {
+ datum->rotation = rotation;
+ datum->rotationSpeed = rotationSpeed;
+ datum->autoRotate = autoRotate;
+ } else {
+ getShadowDatum(datum)->rotation = rotation;
+ getShadowDatum(datum)->rotationSpeed = rotationSpeed;
+ getShadowDatum(datum)->autoRotate = autoRotate;
+ }
+ }
+ case Colored:
+ //Color initialization
+ // Particle color
+ if (m_explicitColor) {
+ if (!datum->colorOwner)
+ datum->colorOwner = this;
+ color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
+ color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
+ color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
+ color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
+ if (datum->colorOwner == this)
+ datum->color = color;
+ else
+ getShadowDatum(datum)->color = color;
+ }
+ default:
+ break;
+ }
+}
+
+void QQuickImageParticle::commit(int gIdx, int pIdx)
+{
+ if (m_pleaseReset)
+ return;
+ QSGGeometryNode *node = m_nodes[gIdx];
+ if (!node)
+ return;
+ QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
+ DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
+ ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
+ SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
+ switch (perfLevel){//No automatic fall through intended on this one
+ case Sprites:
+ spriteVertices += pIdx*4;
+ for (int i=0; i<4; i++){
+ spriteVertices[i].x = datum->x - m_systemOffset.x();
+ spriteVertices[i].y = datum->y - m_systemOffset.y();
+ spriteVertices[i].t = datum->t;
+ spriteVertices[i].lifeSpan = datum->lifeSpan;
+ spriteVertices[i].size = datum->size;
+ spriteVertices[i].endSize = datum->endSize;
+ spriteVertices[i].vx = datum->vx;
+ spriteVertices[i].vy = datum->vy;
+ spriteVertices[i].ax = datum->ax;
+ spriteVertices[i].ay = datum->ay;
+ if (m_explicitDeformation && datum->deformationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ spriteVertices[i].xx = shadow->xx;
+ spriteVertices[i].xy = shadow->xy;
+ spriteVertices[i].yx = shadow->yx;
+ spriteVertices[i].yy = shadow->yy;
+ } else {
+ spriteVertices[i].xx = datum->xx;
+ spriteVertices[i].xy = datum->xy;
+ spriteVertices[i].yx = datum->yx;
+ spriteVertices[i].yy = datum->yy;
+ }
+ if (m_explicitRotation && datum->rotationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ spriteVertices[i].rotation = shadow->rotation;
+ spriteVertices[i].rotationSpeed = shadow->rotationSpeed;
+ spriteVertices[i].autoRotate = shadow->autoRotate;
+ } else {
+ spriteVertices[i].rotation = datum->rotation;
+ spriteVertices[i].rotationSpeed = datum->rotationSpeed;
+ spriteVertices[i].autoRotate = datum->autoRotate;
+ }
+ //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
+ if (m_explicitColor && datum->colorOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ spriteVertices[i].color.r = shadow->color.r;
+ spriteVertices[i].color.g = shadow->color.g;
+ spriteVertices[i].color.b = shadow->color.b;
+ spriteVertices[i].color.a = shadow->color.a;
+ } else {
+ spriteVertices[i].color.r = datum->color.r;
+ spriteVertices[i].color.g = datum->color.g;
+ spriteVertices[i].color.b = datum->color.b;
+ spriteVertices[i].color.a = datum->color.a;
+ }
+ }
+ break;
+ case Tabled: //Fall through until it has its own vertex class
+ case Deformable:
+ deformableVertices += pIdx*4;
+ for (int i=0; i<4; i++){
+ deformableVertices[i].x = datum->x - m_systemOffset.x();
+ deformableVertices[i].y = datum->y - m_systemOffset.y();
+ deformableVertices[i].t = datum->t;
+ deformableVertices[i].lifeSpan = datum->lifeSpan;
+ deformableVertices[i].size = datum->size;
+ deformableVertices[i].endSize = datum->endSize;
+ deformableVertices[i].vx = datum->vx;
+ deformableVertices[i].vy = datum->vy;
+ deformableVertices[i].ax = datum->ax;
+ deformableVertices[i].ay = datum->ay;
+ if (m_explicitDeformation && datum->deformationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ deformableVertices[i].xx = shadow->xx;
+ deformableVertices[i].xy = shadow->xy;
+ deformableVertices[i].yx = shadow->yx;
+ deformableVertices[i].yy = shadow->yy;
+ } else {
+ deformableVertices[i].xx = datum->xx;
+ deformableVertices[i].xy = datum->xy;
+ deformableVertices[i].yx = datum->yx;
+ deformableVertices[i].yy = datum->yy;
+ }
+ if (m_explicitRotation && datum->rotationOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ deformableVertices[i].rotation = shadow->rotation;
+ deformableVertices[i].rotationSpeed = shadow->rotationSpeed;
+ deformableVertices[i].autoRotate = shadow->autoRotate;
+ } else {
+ deformableVertices[i].rotation = datum->rotation;
+ deformableVertices[i].rotationSpeed = datum->rotationSpeed;
+ deformableVertices[i].autoRotate = datum->autoRotate;
+ }
+ if (m_explicitColor && datum->colorOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ deformableVertices[i].color.r = shadow->color.r;
+ deformableVertices[i].color.g = shadow->color.g;
+ deformableVertices[i].color.b = shadow->color.b;
+ deformableVertices[i].color.a = shadow->color.a;
+ } else {
+ deformableVertices[i].color.r = datum->color.r;
+ deformableVertices[i].color.g = datum->color.g;
+ deformableVertices[i].color.b = datum->color.b;
+ deformableVertices[i].color.a = datum->color.a;
+ }
+ }
+ break;
+ case Colored:
+ coloredVertices += pIdx*1;
+ for (int i=0; i<1; i++){
+ coloredVertices[i].x = datum->x - m_systemOffset.x();
+ coloredVertices[i].y = datum->y - m_systemOffset.y();
+ coloredVertices[i].t = datum->t;
+ coloredVertices[i].lifeSpan = datum->lifeSpan;
+ coloredVertices[i].size = datum->size;
+ coloredVertices[i].endSize = datum->endSize;
+ coloredVertices[i].vx = datum->vx;
+ coloredVertices[i].vy = datum->vy;
+ coloredVertices[i].ax = datum->ax;
+ coloredVertices[i].ay = datum->ay;
+ if (m_explicitColor && datum->colorOwner != this) {
+ QQuickParticleData* shadow = getShadowDatum(datum);
+ coloredVertices[i].color.r = shadow->color.r;
+ coloredVertices[i].color.g = shadow->color.g;
+ coloredVertices[i].color.b = shadow->color.b;
+ coloredVertices[i].color.a = shadow->color.a;
+ } else {
+ coloredVertices[i].color.r = datum->color.r;
+ coloredVertices[i].color.g = datum->color.g;
+ coloredVertices[i].color.b = datum->color.b;
+ coloredVertices[i].color.a = datum->color.a;
+ }
+ }
+ break;
+ case Simple:
+ simpleVertices += pIdx*1;
+ for (int i=0; i<1; i++){
+ simpleVertices[i].x = datum->x - m_systemOffset.x();
+ simpleVertices[i].y = datum->y - m_systemOffset.y();
+ simpleVertices[i].t = datum->t;
+ simpleVertices[i].lifeSpan = datum->lifeSpan;
+ simpleVertices[i].size = datum->size;
+ simpleVertices[i].endSize = datum->endSize;
+ simpleVertices[i].vx = datum->vx;
+ simpleVertices[i].vy = datum->vy;
+ simpleVertices[i].ax = datum->ax;
+ simpleVertices[i].ay = datum->ay;
+ }
+ break;
+ default:
+ break;
+ }
+
+ node->setFlag(QSGNode::OwnsGeometry, true);
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h
new file mode 100644
index 0000000000..4db2c9801a
--- /dev/null
+++ b/src/particles/qquickimageparticle_p.h
@@ -0,0 +1,446 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ULTRAPARTICLE_H
+#define ULTRAPARTICLE_H
+#include "qquickparticlepainter_p.h"
+#include "qquickdirection_p.h"
+#include <private/qquickpixmapcache_p.h>
+#include <QQmlListProperty>
+#include <QtQuick/qsgsimplematerial.h>
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class ImageMaterialData;
+class QSGGeometryNode;
+
+class QQuickSprite;
+class QQuickStochasticEngine;
+
+struct SimpleVertex {
+ float x;
+ float y;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+};
+
+struct ColoredVertex {
+ float x;
+ float y;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+ Color4ub color;
+};
+
+struct DeformableVertex {
+ float x;
+ float y;
+ float tx;
+ float ty;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+ Color4ub color;
+ float xx;
+ float xy;
+ float yx;
+ float yy;
+ float rotation;
+ float rotationSpeed;
+ float autoRotate;//Assumed that GPUs prefer floats to bools
+};
+
+struct SpriteVertex {
+ float x;
+ float y;
+ float tx;
+ float ty;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+ Color4ub color;
+ float xx;
+ float xy;
+ float yx;
+ float yy;
+ float rotation;
+ float rotationSpeed;
+ float autoRotate;//Assumed that GPUs prefer floats to bools
+ float animW;
+ float animH;
+ float animProgress;
+ float animX1;
+ float animY1;
+ float animX2;
+ float animY2;
+};
+
+template <typename Vertex>
+struct Vertices {
+ Vertex v1;
+ Vertex v2;
+ Vertex v3;
+ Vertex v4;
+};
+
+class QQuickImageParticle : public QQuickParticlePainter
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ image WRITE setImage NOTIFY imageChanged)
+ Q_PROPERTY(QQmlListProperty<QQuickSprite> sprites READ sprites)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ //### Is it worth having progress like Image has?
+ //Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
+
+ Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged)
+ Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged)
+ Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged)
+
+ //###Now just colorize - add a flag for 'solid' color particles(where the img is just a mask?)?
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged RESET resetColor)
+ //Stacks (added) with individual colorVariations
+ Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged RESET resetColor)
+ Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged RESET resetColor)
+ Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged RESET resetColor)
+ Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged RESET resetColor)
+ //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha)
+ Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged RESET resetColor)
+ Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged RESET resetColor)
+
+ Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged RESET resetRotation)
+ Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged RESET resetRotation)
+ Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged RESET resetRotation)
+ Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged RESET resetRotation)
+ //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation
+ //to 180 will lead to facing away from the direction of motion
+ Q_PROPERTY(bool autoRotation READ autoRotation WRITE setAutoRotation NOTIFY autoRotationChanged RESET resetRotation)
+
+ //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size
+ Q_PROPERTY(QQuickDirection* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged RESET resetDeformation)
+ //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram.
+ Q_PROPERTY(QQuickDirection* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged RESET resetDeformation)
+ Q_PROPERTY(bool spritesInterpolate READ spritesInterpolate WRITE setSpritesInterpolate NOTIFY spritesInterpolateChanged)
+
+ Q_PROPERTY(EntryEffect entryEffect READ entryEffect WRITE setEntryEffect NOTIFY entryEffectChanged)
+ Q_ENUMS(EntryEffect)
+ Q_ENUMS(Status)
+public:
+ explicit QQuickImageParticle(QQuickItem *parent = 0);
+ virtual ~QQuickImageParticle();
+
+ enum Status { Null, Ready, Loading, Error };
+
+ QQmlListProperty<QQuickSprite> sprites();
+ QQuickStochasticEngine* spriteEngine() {return m_spriteEngine;}
+
+ enum EntryEffect {
+ None = 0,
+ Fade = 1,
+ Scale = 2
+ };
+
+ enum PerformanceLevel{//TODO: Expose?
+ Unknown = 0,
+ Simple,
+ Colored,
+ Deformable,
+ Tabled,
+ Sprites
+ };
+
+ QUrl image() const { return m_image ? m_image->source : QUrl(); }
+ void setImage(const QUrl &image);
+
+ QUrl colortable() const { return m_colorTable ? m_colorTable->source : QUrl(); }
+ void setColortable(const QUrl &table);
+
+ QUrl sizetable() const { return m_sizeTable ? m_sizeTable->source : QUrl(); }
+ void setSizetable (const QUrl &table);
+
+ QUrl opacitytable() const { return m_opacityTable ? m_opacityTable->source : QUrl(); }
+ void setOpacitytable(const QUrl &table);
+
+ QColor color() const { return m_color; }
+ void setColor(const QColor &color);
+
+ qreal colorVariation() const { return m_color_variation; }
+ void setColorVariation(qreal var);
+
+ qreal alphaVariation() const { return m_alphaVariation; }
+
+ qreal alpha() const { return m_alpha; }
+
+ qreal redVariation() const { return m_redVariation; }
+
+ qreal greenVariation() const { return m_greenVariation; }
+
+ qreal blueVariation() const { return m_blueVariation; }
+
+ qreal rotation() const { return m_rotation; }
+
+ qreal rotationVariation() const { return m_rotationVariation; }
+
+ qreal rotationSpeed() const { return m_rotationSpeed; }
+
+ qreal rotationSpeedVariation() const { return m_rotationSpeedVariation; }
+
+ bool autoRotation() const { return m_autoRotation; }
+
+ QQuickDirection* xVector() const { return m_xVector; }
+
+ QQuickDirection* yVector() const { return m_yVector; }
+
+ bool spritesInterpolate() const { return m_spritesInterpolate; }
+
+ bool bypassOptimizations() const { return m_bypassOptimizations; }
+
+ EntryEffect entryEffect() const { return m_entryEffect; }
+
+ Status status() const { return m_status; }
+
+ void resetColor();
+ void resetRotation();
+ void resetDeformation();
+
+signals:
+
+ void imageChanged();
+ void colortableChanged();
+ void sizetableChanged();
+ void opacitytableChanged();
+
+ void colorChanged();
+ void colorVariationChanged();
+
+ void alphaVariationChanged(qreal arg);
+
+ void alphaChanged(qreal arg);
+
+ void redVariationChanged(qreal arg);
+
+ void greenVariationChanged(qreal arg);
+
+ void blueVariationChanged(qreal arg);
+
+ void rotationChanged(qreal arg);
+
+ void rotationVariationChanged(qreal arg);
+
+ void rotationSpeedChanged(qreal arg);
+
+ void rotationSpeedVariationChanged(qreal arg);
+
+ void autoRotationChanged(bool arg);
+
+ void xVectorChanged(QQuickDirection* arg);
+
+ void yVectorChanged(QQuickDirection* arg);
+
+ void spritesInterpolateChanged(bool arg);
+
+ void bypassOptimizationsChanged(bool arg);
+
+ void entryEffectChanged(EntryEffect arg);
+
+ void statusChanged(Status arg);
+
+public slots:
+ void reloadColor(const Color4ub &c, QQuickParticleData* d);
+ void setAlphaVariation(qreal arg);
+
+ void setAlpha(qreal arg);
+
+ void setRedVariation(qreal arg);
+
+ void setGreenVariation(qreal arg);
+
+ void setBlueVariation(qreal arg);
+
+ void setRotation(qreal arg);
+
+ void setRotationVariation(qreal arg);
+
+ void setRotationSpeed(qreal arg);
+
+ void setRotationSpeedVariation(qreal arg);
+
+ void setAutoRotation(bool arg);
+
+ void setXVector(QQuickDirection* arg);
+
+ void setYVector(QQuickDirection* arg);
+
+ void setSpritesInterpolate(bool arg);
+
+ void setBypassOptimizations(bool arg);
+
+ void setEntryEffect(EntryEffect arg);
+
+protected:
+ void reset();
+ virtual void initialize(int gIdx, int pIdx);
+ virtual void commit(int gIdx, int pIdx);
+
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+ void prepareNextFrame();
+ void buildParticleNodes();
+
+ void sceneGraphInvalidated();
+
+private slots:
+ void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
+
+ void spriteAdvance(int spriteIndex);
+ void spritesUpdate(qreal time = 0 );
+ void finishBuildParticleNodes();
+private:
+ struct ImageData {
+ QUrl source;
+ QQuickPixmap pix;
+ };
+ ImageData *m_image;
+ ImageData *m_colorTable;
+ ImageData *m_sizeTable;
+ ImageData *m_opacityTable;
+ bool loadingSomething();
+
+
+ QColor m_color;
+ qreal m_color_variation;
+
+ QSGGeometryNode *m_rootNode;
+ QHash<int, QSGGeometryNode *> m_nodes;
+ QHash<int, int> m_idxStarts;//TODO: Proper resizing will lead to needing a spriteEngine per particle - do this after sprite engine gains transparent sharing?
+ QList<QPair<int, int> > m_startsIdx;//Same data, optimized for alternate retrieval
+
+ int m_lastIdxStart;
+ QSGMaterial *m_material;
+
+ // derived values...
+
+ qreal m_alphaVariation;
+ qreal m_alpha;
+ qreal m_redVariation;
+ qreal m_greenVariation;
+ qreal m_blueVariation;
+ qreal m_rotation;
+ qreal m_rotationVariation;
+ qreal m_rotationSpeed;
+ qreal m_rotationSpeedVariation;
+ bool m_autoRotation;
+ QQuickDirection* m_xVector;
+ QQuickDirection* m_yVector;
+
+ QList<QQuickSprite*> m_sprites;
+ QQuickSpriteEngine* m_spriteEngine;
+ bool m_spritesInterpolate;
+
+ bool m_explicitColor;
+ bool m_explicitRotation;
+ bool m_explicitDeformation;
+ bool m_explicitAnimation;
+ QHash<int, QVector<QQuickParticleData*> > m_shadowData;
+ void clearShadows();
+ QQuickParticleData* getShadowDatum(QQuickParticleData* datum);
+
+ bool m_bypassOptimizations;
+ PerformanceLevel perfLevel;
+
+ PerformanceLevel m_lastLevel;
+ bool m_debugMode;
+
+ template<class Vertex>
+ void initTexCoords(Vertex* v, int count){
+ Vertex* end = v + count;
+ while (v < end){
+ v[0].tx = 0;
+ v[0].ty = 0;
+
+ v[1].tx = 1;
+ v[1].ty = 0;
+
+ v[2].tx = 0;
+ v[2].ty = 1;
+
+ v[3].tx = 1;
+ v[3].ty = 1;
+
+ v += 4;
+ }
+ }
+
+ template<class MaterialData>
+ MaterialData* getState(QSGMaterial* m){
+ return static_cast<QSGSimpleMaterial<MaterialData> *>(m)->state();
+ }
+ EntryEffect m_entryEffect;
+ Status m_status;
+ bool m_buildingNodes;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // ULTRAPARTICLE_H
diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp
new file mode 100644
index 0000000000..91ef06fcb2
--- /dev/null
+++ b/src/particles/qquickitemparticle.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickitemparticle_p.h"
+#include <private/qquickvisualitemmodel_p.h>
+#include <QtQuick/qsgnode.h>
+#include <QTimer>
+#include <QQmlComponent>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass ItemParticle QQuickItemParticle
+ \inqmlmodule QtQuick.Particles 2
+ \inherits ParticlePainter
+ \brief The ItemParticle element allows you to specify your own delegate to paint particles.
+
+*/
+
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ItemParticle::freeze(Item item)
+
+ Suspends the flow of time for the logical particle which item represents, allowing you to control its movement.
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ItemParticle::unfreeze(Item item)
+
+ Restarts the flow of time for the logical particle which item represents, allowing it to be moved by the particle system again.
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ItemParticle::take(Item item, bool prioritize)
+
+ Asks the ItemParticle to take over control of item. It will be emitted when there is a logical particle available.
+
+ By default items form a queue when waiting for a logical particle, but if prioritize is true then it will go immediately to the
+ head of the queue.
+*/
+/*!
+ \qmlmethod void QtQuick.Particles2::ItemParticle::give(Item item)
+
+ Orders the ItemParticle to give you control of the item. It will cease controlling it and the item will lose its association to the logical particle.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::ItemParticle::fade
+
+ If true, the item will automatically be faded in and out
+ at the ends of its lifetime. If false, you will have to
+ implement any entry effect yourself.
+
+ Default is true.
+*/
+/*!
+ \qmlproperty Component QtQuick.Particles2::ItemParticle::delegate
+
+ An instance of the delegate will be created for every logical
+ particle, and moved along with it.
+*/
+
+QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) :
+ QQuickParticlePainter(parent), m_fade(true), m_delegate(0)
+{
+ setFlag(QQuickItem::ItemHasContents);
+ clock = new Clock(this);
+ clock->start();
+}
+
+QQuickItemParticle::~QQuickItemParticle()
+{
+ delete clock;
+}
+
+void QQuickItemParticle::freeze(QQuickItem* item)
+{
+ m_stasis << item;
+}
+
+
+void QQuickItemParticle::unfreeze(QQuickItem* item)
+{
+ m_stasis.remove(item);
+}
+
+void QQuickItemParticle::take(QQuickItem *item, bool prioritize)
+{
+ if (prioritize)
+ m_pendingItems.push_front(item);
+ else
+ m_pendingItems.push_back(item);
+}
+
+void QQuickItemParticle::give(QQuickItem *item)
+{
+ //TODO: This
+ Q_UNUSED(item);
+}
+
+void QQuickItemParticle::initialize(int gIdx, int pIdx)
+{
+ m_loadables << m_system->groupData[gIdx]->data[pIdx];//defer to other thread
+}
+
+void QQuickItemParticle::commit(int, int)
+{
+}
+
+void QQuickItemParticle::tick(int time)
+{
+ Q_UNUSED(time);//only needed because QTickAnimationProxy expects one
+ foreach (QQuickItem* item, m_deletables){
+ if (m_fade)
+ item->setOpacity(0.);
+ item->setVisible(false);
+ QQuickItemParticleAttached* mpa;
+ if ((mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(item))))
+ mpa->detach();//reparent as well?
+ //TODO: Delete iff we created it
+ m_activeCount--;
+ }
+ m_deletables.clear();
+
+ foreach (QQuickParticleData* d, m_loadables){
+ if (m_stasis.contains(d->delegate))
+ qWarning() << "Current model particles prefers overwrite:false";
+ //remove old item from the particle that is dying to make room for this one
+ if (d->delegate)
+ m_deletables << d->delegate;
+ d->delegate = 0;
+ if (!m_pendingItems.isEmpty()){
+ d->delegate = m_pendingItems.front();
+ m_pendingItems.pop_front();
+ }else if (m_delegate){
+ d->delegate = qobject_cast<QQuickItem*>(m_delegate->create(qmlContext(this)));
+ }
+ if (d->delegate && d){//###Data can be zero if creating an item leads to a reset - this screws things up.
+ d->delegate->setX(d->curX() - d->delegate->width()/2);//TODO: adjust for system?
+ d->delegate->setY(d->curY() - d->delegate->height()/2);
+ QQuickItemParticleAttached* mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(d->delegate));
+ if (mpa){
+ mpa->m_mp = this;
+ mpa->attach();
+ }
+ d->delegate->setParentItem(this);
+ if (m_fade)
+ d->delegate->setOpacity(0.);
+ d->delegate->setVisible(false);//Will be set to true when we prepare the next frame
+ m_activeCount++;
+ }
+ }
+ m_loadables.clear();
+}
+
+void QQuickItemParticle::reset()
+{
+ QQuickParticlePainter::reset();
+ m_loadables.clear();
+ //TODO: Cleanup items?
+ //deletables?
+}
+
+
+QSGNode* QQuickItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d)
+{
+ //Dummy update just to get painting tick
+ if (m_pleaseReset){
+ m_pleaseReset = false;
+ //Refill loadables, delayed here so as to only happen once per frame max
+ //### Constant resetting might lead to m_loadables never being populated when tick() occurs
+ foreach (const QString group, m_groups){
+ int gIdx = m_system->groupIds[group];
+ foreach (QQuickParticleData* d, m_system->groupData[gIdx]->data)
+ if (!d->delegate && d->t != -1 && d->stillAlive())
+ m_loadables << d;
+ }
+ }
+ prepareNextFrame();
+
+ update();//Get called again
+ if (n)
+ n->markDirty(QSGNode::DirtyMaterial);
+ return QQuickItem::updatePaintNode(n,d);
+}
+
+void QQuickItemParticle::prepareNextFrame()
+{
+ if (!m_system)
+ return;
+ qint64 timeStamp = m_system->systemSync(this);
+ qreal curT = timeStamp/1000.0;
+ qreal dt = curT - m_lastT;
+ m_lastT = curT;
+ if (!m_activeCount)
+ return;
+
+ //TODO: Size, better fade?
+ foreach (const QString &str, m_groups){
+ int gIdx = m_system->groupIds[str];
+ int count = m_system->groupData[gIdx]->size();
+
+ for (int i=0; i<count; i++){
+ QQuickParticleData* data = m_system->groupData[gIdx]->data[i];
+ QQuickItem* item = data->delegate;
+ if (!item)
+ continue;
+ qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan;
+ if (m_stasis.contains(item)) {
+ data->t += dt;//Stasis effect
+ continue;
+ }
+ if (t >= 1.0){//Usually happens from load
+ m_deletables << item;
+ data->delegate = 0;
+ }else{//Fade
+ data->delegate->setVisible(true);
+ if (m_fade){
+ qreal o = 1.;
+ if (t<0.2)
+ o = t*5;
+ if (t>0.8)
+ o = (1-t)*5;
+ item->setOpacity(o);
+ }
+ }
+ item->setX(data->curX() - item->width()/2 - m_systemOffset.x());
+ item->setY(data->curY() - item->height()/2 - m_systemOffset.y());
+ }
+ }
+}
+
+QQuickItemParticleAttached *QQuickItemParticle::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickItemParticleAttached(object);
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickitemparticle_p.h b/src/particles/qquickitemparticle_p.h
new file mode 100644
index 0000000000..c7b8a2661e
--- /dev/null
+++ b/src/particles/qquickitemparticle_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ITEMPARTICLE_H
+#define ITEMPARTICLE_H
+#include "qquickparticlepainter_p.h"
+#include <QPointer>
+#include <QSet>
+#include <private/qquickanimation_p_p.h>
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickVisualDataModel;
+class QQuickItemParticleAttached;
+
+class QQuickItemParticle : public QQuickParticlePainter
+{
+ Q_OBJECT
+ Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged)
+ Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+public:
+ explicit QQuickItemParticle(QQuickItem *parent = 0);
+ ~QQuickItemParticle();
+
+ bool fade() const { return m_fade; }
+
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+ static QQuickItemParticleAttached *qmlAttachedProperties(QObject *object);
+ QQmlComponent* delegate() const
+ {
+ return m_delegate;
+ }
+
+signals:
+ void fadeChanged();
+
+ void delegateChanged(QQmlComponent* arg);
+
+public slots:
+ //TODO: Add a follow mode, where moving the delegate causes the logical particle to go with it?
+ void freeze(QQuickItem* item);
+ void unfreeze(QQuickItem* item);
+ void take(QQuickItem* item,bool prioritize=false);//take by modelparticle
+ void give(QQuickItem* item);//give from modelparticle
+
+ void setFade(bool arg){if (arg == m_fade) return; m_fade = arg; emit fadeChanged();}
+ void setDelegate(QQmlComponent* arg)
+ {
+ if (m_delegate != arg) {
+ m_delegate = arg;
+ emit delegateChanged(arg);
+ }
+ }
+
+protected:
+ virtual void reset();
+ virtual void commit(int gIdx, int pIdx);
+ virtual void initialize(int gIdx, int pIdx);
+ void prepareNextFrame();
+private:
+ void tick(int time = 0);
+ QList<QQuickItem* > m_deletables;
+ QList< QQuickParticleData* > m_loadables;
+ bool m_fade;
+
+ QList<QQuickItem*> m_pendingItems;
+ QList<int> m_available;
+ QSet<QQuickItem*> m_stasis;
+ qreal m_lastT;
+ int m_activeCount;
+ QQmlComponent* m_delegate;
+
+ typedef QTickAnimationProxy<QQuickItemParticle, &QQuickItemParticle::tick> Clock;
+ Clock *clock;
+};
+
+class QQuickItemParticleAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickItemParticle* particle READ particle CONSTANT);
+public:
+ QQuickItemParticleAttached(QObject* parent)
+ : QObject(parent), m_mp(0)
+ {;}
+ QQuickItemParticle* particle() {return m_mp;}
+ void detach(){emit detached();}
+ void attach(){emit attached();}
+private:
+ QQuickItemParticle* m_mp;
+ friend class QQuickItemParticle;
+Q_SIGNALS:
+ void detached();
+ void attached();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPEINFO(QQuickItemParticle, QML_HAS_ATTACHED_PROPERTIES)
+
+QT_END_HEADER
+#endif // ITEMPARTICLE_H
diff --git a/src/particles/qquicklineextruder.cpp b/src/particles/qquicklineextruder.cpp
new file mode 100644
index 0000000000..f555de3f72
--- /dev/null
+++ b/src/particles/qquicklineextruder.cpp
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qquicklineextruder_p.h"
+#include <stdlib.h>
+#include <cmath>
+
+/*!
+ \qmlclass LineShape QQuickLineExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Shape
+ \brief The LineShape represents a line to Affectors and Emitter
+
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::LineShape::mirrored
+
+ By default, the line goes from (0,0) to (width, height) of the item that
+ this shape is being applied to.
+
+ If mirrored is set to true, this will be mirrored along the y axis.
+ The line will then go from (0,height) to (width, 0).
+*/
+
+QQuickLineExtruder::QQuickLineExtruder(QObject *parent) :
+ QQuickParticleExtruder(parent), m_mirrored(false)
+{
+}
+
+QPointF QQuickLineExtruder::extrude(const QRectF &r)
+{
+ qreal x,y;
+ if (!r.height()){
+ x = r.width() * ((qreal)rand())/RAND_MAX;
+ y = 0;
+ }else{
+ y = r.height() * ((qreal)rand())/RAND_MAX;
+ if (!r.width()){
+ x = 0;
+ }else{
+ x = r.width()/r.height() * y;
+ if (m_mirrored)
+ x = r.width() - x;
+ }
+ }
+ return QPointF(x,y);
+}
diff --git a/src/particles/qquicklineextruder_p.h b/src/particles/qquicklineextruder_p.h
new file mode 100644
index 0000000000..6f2b2493a6
--- /dev/null
+++ b/src/particles/qquicklineextruder_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef LINEEXTRUDER_H
+#define LINEEXTRUDER_H
+#include "qquickparticleextruder_p.h"
+
+class QQuickLineExtruder : public QQuickParticleExtruder
+{
+ Q_OBJECT
+ //Default is topleft to bottom right. Flipped makes it topright to bottom left
+ Q_PROPERTY(bool mirrored READ mirrored WRITE setmirrored NOTIFY mirroredChanged)
+
+public:
+ explicit QQuickLineExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ bool mirrored() const
+ {
+ return m_mirrored;
+ }
+
+signals:
+
+ void mirroredChanged(bool arg);
+
+public slots:
+
+ void setmirrored(bool arg)
+ {
+ if (m_mirrored != arg) {
+ m_mirrored = arg;
+ emit mirroredChanged(arg);
+ }
+ }
+private:
+ bool m_mirrored;
+};
+
+#endif // LINEEXTRUDER_H
diff --git a/src/particles/qquickmaskextruder.cpp b/src/particles/qquickmaskextruder.cpp
new file mode 100644
index 0000000000..50b71749c0
--- /dev/null
+++ b/src/particles/qquickmaskextruder.cpp
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickmaskextruder_p.h"
+#include <QtQml/qqml.h>
+#include <QtQml/qqmlinfo.h>
+#include <QImage>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass MaskShape QQuickMaskExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Shape
+ \brief The MaskShape element allows you to represent an image as a shape to affectors and emitters.
+
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::MaskShape::source
+
+ The image to use as the mask. Areas with non-zero opacity
+ will be considered inside the shape.
+*/
+
+
+QQuickMaskExtruder::QQuickMaskExtruder(QObject *parent) :
+ QQuickParticleExtruder(parent)
+ , m_lastWidth(-1)
+ , m_lastHeight(-1)
+{
+}
+
+void QQuickMaskExtruder::setSource(QUrl arg)
+{
+ if (m_source != arg) {
+ m_source = arg;
+
+ m_lastHeight = -1;//Trigger reset
+ m_lastWidth = -1;
+ emit sourceChanged(arg);
+ startMaskLoading();
+ }
+}
+
+void QQuickMaskExtruder::startMaskLoading()
+{
+ m_pix.clear(this);
+ if (m_source.isEmpty())
+ return;
+ m_pix.load(qmlEngine(this), m_source);
+ if (m_pix.isLoading())
+ m_pix.connectFinished(this, SLOT(finishMaskLoading()));
+ else
+ finishMaskLoading();
+}
+
+void QQuickMaskExtruder::finishMaskLoading()
+{
+ if (m_pix.isError())
+ qmlInfo(this) << m_pix.error();
+}
+
+QPointF QQuickMaskExtruder::extrude(const QRectF &r)
+{
+ ensureInitialized(r);
+ if (!m_mask.count() || m_img.isNull())
+ return r.topLeft();
+ const QPointF p = m_mask[rand() % m_mask.count()];
+ //### Should random sub-pixel positioning be added?
+ return p + r.topLeft();
+}
+
+bool QQuickMaskExtruder::contains(const QRectF &bounds, const QPointF &point)
+{
+ ensureInitialized(bounds);//###Current usage patterns WILL lead to different bounds/r calls. Separate list?
+ if (m_img.isNull())
+ return false;
+ QPoint p = point.toPoint() - bounds.topLeft().toPoint();
+ return m_img.rect().contains(p) && (bool)m_img.pixelIndex(p);
+}
+
+void QQuickMaskExtruder::ensureInitialized(const QRectF &r)
+{
+ if (m_lastWidth == r.width() && m_lastHeight == r.height())
+ return;//Same as before
+ if (!m_pix.isReady())
+ return;
+ m_lastWidth = r.width();
+ m_lastHeight = r.height();
+
+ m_mask.clear();
+
+ m_img = m_pix.image().createAlphaMask();
+ m_pix.clear();
+ m_img = m_img.convertToFormat(QImage::Format_Mono);//Else LSB, but I think that's easier
+ m_img = m_img.scaled(r.size().toSize());//TODO: Do they need aspect ratio stuff? Or tiling?
+ for (int i=0; i<r.width(); i++){
+ for (int j=0; j<r.height(); j++){
+ if (m_img.pixelIndex(i,j))//Direct bit manipulation is presumably more efficient
+ m_mask << QPointF(i,j);
+ }
+ }
+}
+QT_END_NAMESPACE
diff --git a/src/particles/qquickmaskextruder_p.h b/src/particles/qquickmaskextruder_p.h
new file mode 100644
index 0000000000..8b6c3f0b2d
--- /dev/null
+++ b/src/particles/qquickmaskextruder_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MASKEXTRUDER_H
+#define MASKEXTRUDER_H
+#include "qquickparticleextruder_p.h"
+#include <private/qquickpixmapcache_p.h>
+#include <QUrl>
+#include <QImage>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickMaskExtruder : public QQuickParticleExtruder
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+public:
+ explicit QQuickMaskExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ virtual bool contains(const QRectF &bounds, const QPointF &point);
+
+ QUrl source() const
+ {
+ return m_source;
+ }
+
+signals:
+
+ void sourceChanged(QUrl arg);
+
+public slots:
+ void setSource(QUrl arg);
+
+private slots:
+ void startMaskLoading();
+ void finishMaskLoading();
+
+private:
+ QUrl m_source;
+
+ void ensureInitialized(const QRectF &r);
+ int m_lastWidth;
+ int m_lastHeight;
+ QQuickPixmap m_pix;
+ QImage m_img;
+ QList<QPointF> m_mask;//TODO: More memory efficient datastructures
+ //Perhaps just the mask for the largest bounds is stored, and interpolate up
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // MASKEXTRUDER_H
diff --git a/src/particles/qquickparticleaffector.cpp b/src/particles/qquickparticleaffector.cpp
new file mode 100644
index 0000000000..0005af86af
--- /dev/null
+++ b/src/particles/qquickparticleaffector.cpp
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticleaffector_p.h"
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass Affector QQuickParticleAffector
+ \inqmlmodule QtQuick.Particles 2
+ \brief Affector elements can alter the attributes of logical particles at any point in their lifetime.
+
+ The base Affector does not alter any attributes, but can be used to emit a signal
+ when a particle meets certain conditions.
+
+ If an affector has a defined size, then it will only affect particles within its size and position on screen.
+
+ Affectors have different performance characteristics to the other particle system elements. In particular,
+ they have some simplifications to try to maintain a simulation at real-time or faster. When running a system
+ with Affectors, irregular frame timings that grow too large ( > one second per frame) will cause the Affectors
+ to try and cut corners with a faster but less accurate simulation. If the system has multiple affectors the order
+ in which they are applied is not guaranteed, and when simulating larger time shifts they will simulate the whole
+ shift each, which can lead to different results compared to smaller time shifts.
+
+ Accurate simulation for large numbers of particles (hundreds) with multiple affectors may be possible on some hardware,
+ but on less capable hardware you should expect small irregularties in the simulation as simulates with worse granularity.
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::Affector::system
+ This is the system which will be affected by the element.
+ If the Affector is a direct child of a ParticleSystem, it will automatically be associated with it.
+*/
+/*!
+ \qmlproperty list<string> QtQuick.Particles2::Affector::groups
+ Which logical particle groups will be affected.
+
+ If empty, it will affect all particles.
+*/
+/*!
+ \qmlproperty list<string> QtQuick.Particles2::Affector::whenCollidingWith
+ If any logical particle groups are specified here, then the affector
+ will only be triggered if the particle being examined intersects with
+ a particle of one of these groups.
+
+ This is different from the groups property. The groups property selects which
+ particles might be examined, and if they meet other criteria (including being
+ within the bounds of the Affector, modified by shape) then they will be tested
+ again to see if they intersect with a particles from one of the particle groups
+ in whenCollidingWith.
+
+ By default, no groups are specified.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::Affector::enabled
+ If enabled is set to false, this affector will not affect any particles.
+
+ Usually this is used to conditionally turn an affector on or off.
+
+ Default value is true.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::Affector::once
+ If once is set to true, this affector will only affect each particle
+ once in their lifetimes. If the affector normally simulates a continuous
+ effect over time, then it will simulate the effect of one second of time
+ the one instant it affects the particle.
+
+ Default value is false.
+*/
+/*!
+ \qmlproperty Shape QtQuick.Particles2::Affector::shape
+ If a size has been defined, the shape property can be used to affect a
+ non-rectangular area.
+*/
+/*!
+ \qmlsignal QtQuick.Particles2::Affector::onAffected(x, y)
+
+ This signal is emitted each time the affector actually affects a particle.
+
+ x,y are the coordinates of the affected particle, relative to the ParticleSystem.
+
+*/
+
+/*!
+ \qmlsignal QtQuick.Particles2::Affector::affectParticle(particle particle, real dt)
+
+ This handler is called when particles are selected to be affected.
+
+ dt is the time since the last time it was affected. Use dt to normalize
+ trajectory manipulations to real time.
+
+ Note that JS is slower to execute, so it is not recommended to use this in
+ high-volume particle systems.
+*/
+/*!
+ \qmlsignal QtQuick.Particles2::Affector::affected(real x, real y)
+
+ This handler is called when a particle is selected to be affected. It will
+ only be called if signal is set to true.
+
+ x,y is the particles current position.
+*/
+QQuickParticleAffector::QQuickParticleAffector(QQuickItem *parent) :
+ QQuickItem(parent), m_needsReset(false), m_ignoresTime(false), m_onceOff(false), m_enabled(true)
+ , m_system(0), m_updateIntSet(false), m_shape(new QQuickParticleExtruder(this))
+{
+}
+
+bool QQuickParticleAffector::isAffectedConnected()
+{
+ static int idx = QObjectPrivate::get(this)->signalIndex("affected(qreal,qreal)");
+ return QObjectPrivate::get(this)->isSignalConnected(idx);
+}
+
+
+void QQuickParticleAffector::componentComplete()
+{
+ if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
+ setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
+ QQuickItem::componentComplete();
+}
+
+bool QQuickParticleAffector::activeGroup(int g) {
+ if (m_updateIntSet){ //This can occur before group ids are properly assigned, but that resets the flag
+ m_groupIds.clear();
+ foreach (const QString &p, m_groups)
+ m_groupIds << m_system->groupIds[p];
+ m_updateIntSet = false;
+ }
+ return m_groupIds.isEmpty() || m_groupIds.contains(g);
+}
+
+bool QQuickParticleAffector::shouldAffect(QQuickParticleData* d)
+{
+ if (!d)
+ return false;
+ if (activeGroup(d->group)){
+ if ((m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index)))
+ || !d->stillAlive())
+ return false;
+ //Need to have previous location for affected anyways
+ if (width() == 0 || height() == 0
+ || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()), QPointF(d->curX(), d->curY()))){
+ if (m_whenCollidingWith.isEmpty() || isColliding(d)){
+ return true;
+ }
+ }
+ }
+ return false;
+
+}
+
+void QQuickParticleAffector::postAffect(QQuickParticleData* d)
+{
+ m_system->needsReset << d;
+ if (m_onceOff)
+ m_onceOffed << qMakePair(d->group, d->index);
+ if (isAffectedConnected())
+ emit affected(d->curX(), d->curY());
+}
+
+const qreal QQuickParticleAffector::simulationDelta = 0.020;
+const qreal QQuickParticleAffector::simulationCutoff = 1.000;//If this goes above 1.0, then m_once behaviour needs special codepath
+
+void QQuickParticleAffector::affectSystem(qreal dt)
+{
+ if (!m_enabled)
+ return;
+ //If not reimplemented, calls affectParticle per particle
+ //But only on particles in targeted system/area
+ updateOffsets();//### Needed if an ancestor is transformed.
+ if (m_onceOff)
+ dt = 1.0;
+ foreach (QQuickParticleGroupData* gd, m_system->groupData) {
+ if (activeGroup(m_system->groupData.key(gd))) {
+ foreach (QQuickParticleData* d, gd->data) {
+ if (shouldAffect(d)) {
+ bool affected = false;
+ qreal myDt = dt;
+ if (!m_ignoresTime && myDt < simulationCutoff) {
+ int realTime = m_system->timeInt;
+ m_system->timeInt -= myDt * 1000.0;
+ while (myDt > simulationDelta) {
+ m_system->timeInt += simulationDelta * 1000.0;
+ if (d->alive())//Only affect during the parts it was alive for
+ affected = affectParticle(d, simulationDelta) || affected;
+ myDt -= simulationDelta;
+ }
+ m_system->timeInt = realTime;
+ }
+ if (myDt > 0.0)
+ affected = affectParticle(d, myDt) || affected;
+ if (affected)
+ postAffect(d);
+ }
+ }
+ }
+ }
+}
+
+bool QQuickParticleAffector::affectParticle(QQuickParticleData *, qreal )
+{
+ return true;
+}
+
+void QQuickParticleAffector::reset(QQuickParticleData* pd)
+{//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
+ if (m_onceOff)
+ if (activeGroup(pd->group))
+ m_onceOffed.remove(qMakePair(pd->group, pd->index));
+}
+
+void QQuickParticleAffector::updateOffsets()
+{
+ if (m_system)
+ m_offset = m_system->mapFromItem(this, QPointF(0, 0));
+}
+
+bool QQuickParticleAffector::isColliding(QQuickParticleData *d)
+{
+ qreal myCurX = d->curX();
+ qreal myCurY = d->curY();
+ qreal myCurSize = d->curSize()/2;
+ foreach (const QString &group, m_whenCollidingWith){
+ foreach (QQuickParticleData* other, m_system->groupData[m_system->groupIds[group]]->data){
+ if (!other->stillAlive())
+ continue;
+ qreal otherCurX = other->curX();
+ qreal otherCurY = other->curY();
+ qreal otherCurSize = other->curSize()/2;
+ if ((myCurX + myCurSize > otherCurX - otherCurSize
+ && myCurX - myCurSize < otherCurX + otherCurSize)
+ && (myCurY + myCurSize > otherCurY - otherCurSize
+ && myCurY - myCurSize < otherCurY + otherCurSize))
+ return true;
+ }
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickparticleaffector_p.h b/src/particles/qquickparticleaffector_p.h
new file mode 100644
index 0000000000..4147488f87
--- /dev/null
+++ b/src/particles/qquickparticleaffector_p.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLEAFFECTOR_H
+#define PARTICLEAFFECTOR_H
+
+#include <QObject>
+#include "qquickparticlesystem_p.h"
+#include "qquickparticleextruder_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleAffector : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+ Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
+ Q_PROPERTY(QStringList whenCollidingWith READ whenCollidingWith WRITE setWhenCollidingWith NOTIFY whenCollidingWithChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool once READ onceOff WRITE setOnceOff NOTIFY onceChanged)
+ Q_PROPERTY(QQuickParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged)
+
+public:
+ explicit QQuickParticleAffector(QQuickItem *parent = 0);
+ virtual void affectSystem(qreal dt);
+ virtual void reset(QQuickParticleData*);//As some store their own data per particle?
+ QQuickParticleSystem* system() const
+ {
+ return m_system;
+ }
+
+ QStringList groups() const
+ {
+ return m_groups;
+ }
+
+ bool enabled() const
+ {
+ return m_enabled;
+ }
+
+ bool onceOff() const
+ {
+ return m_onceOff;
+ }
+
+ QQuickParticleExtruder* shape() const
+ {
+ return m_shape;
+ }
+
+ QStringList whenCollidingWith() const
+ {
+ return m_whenCollidingWith;
+ }
+
+signals:
+
+ void systemChanged(QQuickParticleSystem* arg);
+
+ void groupsChanged(QStringList arg);
+
+ void enabledChanged(bool arg);
+
+ void onceChanged(bool arg);
+
+ void shapeChanged(QQuickParticleExtruder* arg);
+
+ void affected(qreal x, qreal y);
+
+ void whenCollidingWithChanged(QStringList arg);
+
+public slots:
+void setSystem(QQuickParticleSystem* arg)
+{
+ if (m_system != arg) {
+ m_system = arg;
+ m_system->registerParticleAffector(this);
+ emit systemChanged(arg);
+ }
+}
+
+void setGroups(QStringList arg)
+{
+ if (m_groups != arg) {
+ m_groups = arg;
+ m_updateIntSet = true;
+ emit groupsChanged(arg);
+ }
+}
+
+void setEnabled(bool arg)
+{
+ if (m_enabled != arg) {
+ m_enabled = arg;
+ emit enabledChanged(arg);
+ }
+}
+
+void setOnceOff(bool arg)
+{
+ if (m_onceOff != arg) {
+ m_onceOff = arg;
+ m_needsReset = true;
+ emit onceChanged(arg);
+ }
+}
+
+void setShape(QQuickParticleExtruder* arg)
+{
+ if (m_shape != arg) {
+ m_shape = arg;
+ emit shapeChanged(arg);
+ }
+}
+
+void setWhenCollidingWith(QStringList arg)
+{
+ if (m_whenCollidingWith != arg) {
+ m_whenCollidingWith = arg;
+ emit whenCollidingWithChanged(arg);
+ }
+}
+public slots:
+ void updateOffsets();
+
+protected:
+ friend class QQuickParticleSystem;
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+ bool m_needsReset:1;//### What is this really saving?
+ bool m_ignoresTime:1;
+ bool m_onceOff:1;
+ bool m_enabled:1;
+
+ QQuickParticleSystem* m_system;
+ QStringList m_groups;
+ bool activeGroup(int g);
+ bool shouldAffect(QQuickParticleData* datum);//Call to do the logic on whether it is affecting that datum
+ void postAffect(QQuickParticleData* datum);//Call to do the post-affect logic on particles which WERE affected(once off, needs reset, affected signal)
+ virtual void componentComplete();
+ QPointF m_offset;
+ bool isAffectedConnected();
+ static const qreal simulationDelta;
+ static const qreal simulationCutoff;
+private:
+ QSet<int> m_groupIds;
+ QSet<QPair<int, int> > m_onceOffed;
+ bool m_updateIntSet;
+
+ QQuickParticleExtruder* m_shape;
+
+ QStringList m_whenCollidingWith;
+
+ bool isColliding(QQuickParticleData* d);
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // PARTICLEAFFECTOR_H
diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp
new file mode 100644
index 0000000000..035d66cbcd
--- /dev/null
+++ b/src/particles/qquickparticleemitter.cpp
@@ -0,0 +1,495 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticleemitter_p.h"
+#include <private/qqmlengine_p.h>
+QT_BEGIN_NAMESPACE
+
+
+/*!
+ \qmlclass Emitter QQuickParticleEmitter
+ \inqmlmodule QtQuick.Particles 2
+ \brief The Emitter element allows you to emit logical particles.
+
+ This element emits logical particles into the ParticleSystem, with the
+ given starting attributes.
+
+ Note that logical particles are not
+ automatically rendered, you will need to have one or more
+ ParticlePainter elements visualizing them.
+
+ Note that the given starting attributes can be modified at any point
+ in the particle's lifetime by any Affector element in the same
+ ParticleSystem. This includes attributes like lifespan.
+*/
+
+
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::Emitter::system
+
+ This is the Particle system that the Emitter will emit into.
+ This can be omitted if the Emitter is a direct child of the ParticleSystem
+*/
+/*!
+ \qmlproperty string QtQuick.Particles2::Emitter::group
+
+ This is the logical particle group which it will emit into.
+
+ Default value is "" (empty string).
+*/
+/*!
+ \qmlproperty Shape QtQuick.Particles2::Emitter::shape
+
+ This shape is applied with the size of the Emitter. Particles will be emitted
+ randomly from any area covered by the shape.
+
+ The default shape is a filled in rectangle, which corresponds to the full bounding
+ box of the Emitter.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::Emitter::emitting
+
+ If set to false, the emitter will cease emissions until it is set to true.
+
+ Default value is true.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Emitter::emitRate
+
+ Number of particles emitted per second.
+
+ Default value is 10 particles per second.
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::Emitter::lifeSpan
+
+ The time in milliseconds each emitted particle should last for.
+
+ If you do not want particles to automatically die after a time, for example if
+ you wish to dispose of them manually, set lifeSpan to Emitter.InfiniteLife.
+
+ lifeSpans greater than or equal to 600000 (10 minutes) will be treated as infinite.
+ Particles with lifeSpans less than or equal to 0 will start out dead.
+
+ Default value is 1000 (one second).
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::Emitter::lifeSpanVariation
+
+ Particle lifespans will vary by up to this much in either direction.
+
+ Default value is 0.
+*/
+
+/*!
+ \qmlproperty int QtQuick.Particles2::Emitter::maximumEmitted
+
+ The maximum number of particles at a time that this emitter will have alive.
+
+ This can be set as a performance optimization (when using burst and pulse) or
+ to stagger emissions.
+
+ If this is set to a number below zero, then there is no maximum limit on the number
+ of particles this emitter can have alive.
+
+ The default value is -1.
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::Emitter::startTime
+
+ If this value is set when the emitter is loaded, then it will emit particles from the
+ past, up to startTime milliseconds ago. These will simulate as if they were emitted then,
+ but will not have any affectors applied to them. Affectors will take effect from the present time.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Emitter::size
+
+ The size in pixels of the particles at the start of their life.
+
+ Default value is 16.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Emitter::endSize
+
+ The size in pixels of the particles at the end of their life. Size will
+ be linearly interpolated during the life of the particle from this value and
+ size. If endSize is -1, then the size of the particle will remain constant at
+ the starting size.
+
+ Default value is -1.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Emitter::sizeVariation
+
+ The size of a particle can vary by this much up or down from size/endSize. The same
+ random addition is made to both size and endSize for a single particle.
+
+ Default value is 0.
+*/
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Emitter::speed
+
+ The starting speed of the particles emitted.
+*/
+/*!
+ \qmlproperty StochasticDirection QtQuick.Particles2::Emitter::acceleration
+
+ The starting acceleraton of the particles emitted.
+*/
+/*!
+ \qmlproperty qreal QtQuick.Particles2::Emitter::speedFromMovement
+
+ If this value is non-zero, then any movement of the emitter will provide additional
+ starting velocity to the particles based on the movement. The additional vector will be the
+ same angle as the emitter's movement, with a magnitude that is the magnitude of the emitters
+ movement multiplied by speedFromMovement.
+
+ Default value is 0.
+*/
+
+/*!
+ \qmlsignal QtQuick.Particles2::Emitter::onEmitParticles(Array particles)
+
+ This handler is called when particles are emitted. particles is a javascript
+ array of Particle objects. You can modify particle attributes directly within the handler.
+
+ Note that JS is slower to execute, so it is not recommended to use this in
+ high-volume particle systems.
+*/
+
+/*! \qmlmethod QtQuick.Particles2::Emitter::burst(int count)
+
+ Emits count particles from this emitter immediately.
+*/
+
+/*! \qmlmethod QtQuick.Particles2::Emitter::burst(int x, int y, int count)
+
+ Emits count particles from this emitter immediately. The particles are emitted
+ as if the Emitter was positioned at x,y but all other properties are the same.
+*/
+
+/*! \qmlmethod QtQuick.Particles2::Emitter::pulse(int duration)
+
+ If the emitter is not enabled, enables it for duration milliseconds and then switches
+ it back off.
+*/
+
+QQuickParticleEmitter::QQuickParticleEmitter(QQuickItem *parent) :
+ QQuickItem(parent)
+ , m_particlesPerSecond(10)
+ , m_particleDuration(1000)
+ , m_particleDurationVariation(0)
+ , m_enabled(true)
+ , m_system(0)
+ , m_extruder(0)
+ , m_defaultExtruder(0)
+ , m_speed(&m_nullVector)
+ , m_acceleration(&m_nullVector)
+ , m_particleSize(16)
+ , m_particleEndSize(-1)
+ , m_particleSizeVariation(0)
+ , m_startTime(0)
+ , m_overwrite(true)
+ , m_pulseLeft(0)
+ , m_maxParticleCount(-1)
+ , m_speed_from_movement(0)
+ , m_reset_last(true)
+ , m_last_timestamp(-1)
+ , m_last_emission(0)
+
+{
+ //TODO: Reset speed/acc back to null vector? Or allow null pointer?
+ connect(this, SIGNAL(maximumEmittedChanged(int)),
+ this, SIGNAL(particleCountChanged()));
+ connect(this, SIGNAL(particlesPerSecondChanged(qreal)),
+ this, SIGNAL(particleCountChanged()));
+ connect(this, SIGNAL(particleDurationChanged(int)),
+ this, SIGNAL(particleCountChanged()));
+}
+
+QQuickParticleEmitter::~QQuickParticleEmitter()
+{
+ if (m_defaultExtruder)
+ delete m_defaultExtruder;
+}
+
+bool QQuickParticleEmitter::isEmitConnected()
+{
+ static int idx = QObjectPrivate::get(this)->signalIndex("emitParticles(QQmlV8Handle)");
+ return QObjectPrivate::get(this)->isSignalConnected(idx);
+}
+
+void QQuickParticleEmitter::componentComplete()
+{
+ if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
+ setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
+ QQuickItem::componentComplete();
+}
+
+void QQuickParticleEmitter::setEnabled(bool arg)
+{
+ if (m_enabled != arg) {
+ m_enabled = arg;
+ emit enabledChanged(arg);
+ }
+}
+
+
+QQuickParticleExtruder* QQuickParticleEmitter::effectiveExtruder()
+{
+ if (m_extruder)
+ return m_extruder;
+ if (!m_defaultExtruder)
+ m_defaultExtruder = new QQuickParticleExtruder;
+ return m_defaultExtruder;
+}
+
+void QQuickParticleEmitter::pulse(int milliseconds)
+{
+ if (!m_enabled)
+ m_pulseLeft = milliseconds;
+}
+
+void QQuickParticleEmitter::burst(int num)
+{
+ m_burstQueue << qMakePair(num, QPointF(x(), y()));
+}
+
+void QQuickParticleEmitter::burst(int num, qreal x, qreal y)
+{
+ m_burstQueue << qMakePair(num, QPointF(x, y));
+}
+
+void QQuickParticleEmitter::setMaxParticleCount(int arg)
+{
+ if (m_maxParticleCount != arg) {
+ if (arg < 0 && m_maxParticleCount >= 0){
+ connect(this, SIGNAL(particlesPerSecondChanged(qreal)),
+ this, SIGNAL(particleCountChanged()));
+ connect(this, SIGNAL(particleDurationChanged(int)),
+ this, SIGNAL(particleCountChanged()));
+ }else if (arg >= 0 && m_maxParticleCount < 0){
+ disconnect(this, SIGNAL(particlesPerSecondChanged(qreal)),
+ this, SIGNAL(particleCountChanged()));
+ disconnect(this, SIGNAL(particleDurationChanged(int)),
+ this, SIGNAL(particleCountChanged()));
+ }
+ m_overwrite = arg < 0;
+ m_maxParticleCount = arg;
+ emit maximumEmittedChanged(arg);
+ }
+}
+
+int QQuickParticleEmitter::particleCount() const
+{
+ if (m_maxParticleCount >= 0)
+ return m_maxParticleCount;
+ return m_particlesPerSecond*((m_particleDuration+m_particleDurationVariation)/1000.0);
+}
+
+void QQuickParticleEmitter::setSpeedFromMovement(qreal t)
+{
+ if (t == m_speed_from_movement)
+ return;
+ m_speed_from_movement = t;
+ emit speedFromMovementChanged();
+}
+
+void QQuickParticleEmitter::reset()
+{
+ m_reset_last = true;
+}
+
+void QQuickParticleEmitter::emitWindow(int timeStamp)
+{
+ if (m_system == 0)
+ return;
+ if ((!m_enabled || !m_particlesPerSecond)&& !m_pulseLeft && m_burstQueue.isEmpty()){
+ m_reset_last = true;
+ return;
+ }
+
+ if (m_reset_last) {
+ m_last_emitter = m_last_last_emitter = QPointF(x(), y());
+ if (m_last_timestamp == -1)
+ m_last_timestamp = (timeStamp - m_startTime)/1000.;
+ else
+ m_last_timestamp = timeStamp/1000.;
+ m_last_emission = m_last_timestamp;
+ m_reset_last = false;
+ m_emitCap = particleCount();
+ }
+
+ if (m_pulseLeft){
+ m_pulseLeft -= timeStamp - m_last_timestamp * 1000.;
+ if (m_pulseLeft < 0){
+ if (!m_enabled)
+ timeStamp += m_pulseLeft;
+ m_pulseLeft = 0;
+ }
+ }
+ qreal time = timeStamp / 1000.;
+ qreal particleRatio = 1. / m_particlesPerSecond;
+ qreal pt = m_last_emission;
+ qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0;
+ if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now
+ pt = time - maxLife;
+
+ qreal opt = pt; // original particle time
+ qreal dt = time - m_last_timestamp; // timestamp delta...
+ if (!dt)
+ dt = 0.000001;
+
+ // emitter difference since last...
+ qreal dex = (x() - m_last_emitter.x());
+ qreal dey = (y() - m_last_emitter.y());
+
+ qreal ax = (m_last_last_emitter.x() + m_last_emitter.x()) / 2;
+ qreal bx = m_last_emitter.x();
+ qreal cx = (x() + m_last_emitter.x()) / 2;
+ qreal ay = (m_last_last_emitter.y() + m_last_emitter.y()) / 2;
+ qreal by = m_last_emitter.y();
+ qreal cy = (y() + m_last_emitter.y()) / 2;
+
+ qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize;
+ qreal emitter_x_offset = m_last_emitter.x() - x();
+ qreal emitter_y_offset = m_last_emitter.y() - y();
+ if (!m_burstQueue.isEmpty() && !m_pulseLeft && !m_enabled)//'outside time' emissions only
+ pt = time;
+
+ QList<QQuickParticleData*> toEmit;
+
+ while ((pt < time && m_emitCap) || !m_burstQueue.isEmpty()) {
+ //int pos = m_last_particle % m_particle_count;
+ QQuickParticleData* datum = m_system->newDatum(m_system->groupIds[m_group], !m_overwrite);
+ if (datum){//actually emit(otherwise we've been asked to skip this one)
+ datum->e = this;//###useful?
+ qreal t = 1 - (pt - opt) / dt;
+ qreal vx =
+ - 2 * ax * (1 - t)
+ + 2 * bx * (1 - 2 * t)
+ + 2 * cx * t;
+ qreal vy =
+ - 2 * ay * (1 - t)
+ + 2 * by * (1 - 2 * t)
+ + 2 * cy * t;
+
+
+ // Particle timestamp
+ datum->t = pt;
+ datum->lifeSpan =
+ (m_particleDuration
+ + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
+ / 1000.0;
+
+ if (datum->lifeSpan >= m_system->maxLife){
+ datum->lifeSpan = m_system->maxLife;
+ m_emitCap--;//emitCap keeps us from reemitting 'infinite' particles after their life. Unless you reset the emitter.
+ }
+
+ // Particle position
+ QRectF boundsRect;
+ if (!m_burstQueue.isEmpty()){
+ boundsRect = QRectF(m_burstQueue.first().second.x() - x(), m_burstQueue.first().second.y() - y(),
+ width(), height());
+ } else {
+ boundsRect = QRectF(emitter_x_offset + dex * (pt - opt) / dt, emitter_y_offset + dey * (pt - opt) / dt
+ , width(), height());
+ }
+ QPointF newPos = effectiveExtruder()->extrude(boundsRect);
+ datum->x = newPos.x();
+ datum->y = newPos.y();
+
+ // Particle speed
+ const QPointF &speed = m_speed->sample(newPos);
+ datum->vx = speed.x()
+ + m_speed_from_movement * vx;
+ datum->vy = speed.y()
+ + m_speed_from_movement * vy;
+
+ // Particle acceleration
+ const QPointF &accel = m_acceleration->sample(newPos);
+ datum->ax = accel.x();
+ datum->ay = accel.y();
+
+ // Particle size
+ float sizeVariation = -m_particleSizeVariation
+ + rand() / float(RAND_MAX) * m_particleSizeVariation * 2;
+
+ float size = qMax((qreal)0.0 , m_particleSize + sizeVariation);
+ float endSize = qMax((qreal)0.0 , sizeAtEnd + sizeVariation);
+
+ datum->size = size;// * float(m_emitting);
+ datum->endSize = endSize;// * float(m_emitting);
+
+ toEmit << datum;
+ }
+ if (m_burstQueue.isEmpty()){
+ pt += particleRatio;
+ }else{
+ m_burstQueue.first().first--;
+ if (m_burstQueue.first().first <= 0)
+ m_burstQueue.pop_front();
+ }
+ }
+
+ if (isEmitConnected()) {
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(QQmlEnginePrivate::getV8Engine(qmlEngine(this))->context());
+ v8::Handle<v8::Array> array = v8::Array::New(toEmit.size());
+ for (int i=0; i<toEmit.size(); i++)
+ array->Set(i, toEmit[i]->v8Value().toHandle());
+
+ emitParticles(QQmlV8Handle::fromHandle(array));//A chance for arbitrary JS changes
+ }
+ foreach (QQuickParticleData* d, toEmit)
+ m_system->emitParticle(d);
+
+ m_last_emission = pt;
+
+ m_last_last_last_emitter = m_last_last_emitter;
+ m_last_last_emitter = m_last_emitter;
+ m_last_emitter = QPointF(x(), y());
+ m_last_timestamp = time;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h
new file mode 100644
index 0000000000..eb9e1fd591
--- /dev/null
+++ b/src/particles/qquickparticleemitter_p.h
@@ -0,0 +1,350 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLEEMITTER_H
+#define PARTICLEEMITTER_H
+
+#include <QtQuick/QQuickItem>
+#include <QDebug>
+#include "qquickparticlesystem_p.h"
+#include "qquickparticleextruder_p.h"
+#include "qquickdirection_p.h"
+
+#include <QList>
+#include <QPair>
+#include <QPointF>
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleEmitter : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+ Q_PROPERTY(QString group READ group WRITE setGroup NOTIFY groupChanged)
+ Q_PROPERTY(QQuickParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
+
+ Q_PROPERTY(qreal emitRate READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged)
+ Q_PROPERTY(int lifeSpan READ particleDuration WRITE setParticleDuration NOTIFY particleDurationChanged)
+ Q_PROPERTY(int lifeSpanVariation READ particleDurationVariation WRITE setParticleDurationVariation NOTIFY particleDurationVariationChanged)
+ Q_PROPERTY(int maximumEmitted READ maxParticleCount WRITE setMaxParticleCount NOTIFY maximumEmittedChanged)
+
+ Q_PROPERTY(qreal size READ particleSize WRITE setParticleSize NOTIFY particleSizeChanged)
+ Q_PROPERTY(qreal endSize READ particleEndSize WRITE setParticleEndSize NOTIFY particleEndSizeChanged)
+ Q_PROPERTY(qreal sizeVariation READ particleSizeVariation WRITE setParticleSizeVariation NOTIFY particleSizeVariationChanged)
+
+ Q_PROPERTY(QQuickDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged)
+ Q_PROPERTY(QQuickDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged)
+ Q_PROPERTY(qreal speedFromMovement READ speedFromMovement WRITE setSpeedFromMovement NOTIFY speedFromMovementChanged)
+
+ Q_ENUMS(Lifetime)
+public:
+ explicit QQuickParticleEmitter(QQuickItem *parent = 0);
+ virtual ~QQuickParticleEmitter();
+ virtual void emitWindow(int timeStamp);
+
+ enum Lifetime {
+ InfiniteLife = QQuickParticleSystem::maxLife
+ };
+
+ bool enabled() const
+ {
+ return m_enabled;
+ }
+
+ qreal particlesPerSecond() const
+ {
+ return m_particlesPerSecond;
+ }
+
+ int particleDuration() const
+ {
+ return m_particleDuration;
+ }
+
+ QQuickParticleSystem* system() const
+ {
+ return m_system;
+ }
+
+ QString group() const
+ {
+ return m_group;
+ }
+
+ int particleDurationVariation() const
+ {
+ return m_particleDurationVariation;
+ }
+
+ qreal speedFromMovement() const { return m_speed_from_movement; }
+ void setSpeedFromMovement(qreal s);
+ virtual void componentComplete();
+signals:
+ void emitParticles(QQmlV8Handle particles);
+ void particlesPerSecondChanged(qreal);
+ void particleDurationChanged(int);
+ void enabledChanged(bool);
+
+ void systemChanged(QQuickParticleSystem* arg);
+
+ void groupChanged(QString arg);
+
+ void particleDurationVariationChanged(int arg);
+
+ void extruderChanged(QQuickParticleExtruder* arg);
+
+ void particleSizeChanged(qreal arg);
+
+ void particleEndSizeChanged(qreal arg);
+
+ void particleSizeVariationChanged(qreal arg);
+
+ void speedChanged(QQuickDirection * arg);
+
+ void accelerationChanged(QQuickDirection * arg);
+
+ void maximumEmittedChanged(int arg);
+ void particleCountChanged();
+
+ void speedFromMovementChanged();
+
+ void startTimeChanged(int arg);
+
+public slots:
+ void pulse(int milliseconds);
+ void burst(int num);
+ void burst(int num, qreal x, qreal y);
+
+ void setEnabled(bool arg);
+
+ void setParticlesPerSecond(qreal arg)
+ {
+ if (m_particlesPerSecond != arg) {
+ m_particlesPerSecond = arg;
+ emit particlesPerSecondChanged(arg);
+ }
+ }
+
+ void setParticleDuration(int arg)
+ {
+ if (m_particleDuration != arg) {
+ m_particleDuration = arg;
+ emit particleDurationChanged(arg);
+ }
+ }
+
+ void setSystem(QQuickParticleSystem* arg)
+ {
+ if (m_system != arg) {
+ m_system = arg;
+ if (m_system)
+ m_system->registerParticleEmitter(this);
+ emit systemChanged(arg);
+ }
+ }
+
+ void setGroup(QString arg)
+ {
+ if (m_group != arg) {
+ m_group = arg;
+ emit groupChanged(arg);
+ }
+ }
+
+ void setParticleDurationVariation(int arg)
+ {
+ if (m_particleDurationVariation != arg) {
+ m_particleDurationVariation = arg;
+ emit particleDurationVariationChanged(arg);
+ }
+ }
+ void setExtruder(QQuickParticleExtruder* arg)
+ {
+ if (m_extruder != arg) {
+ m_extruder = arg;
+ emit extruderChanged(arg);
+ }
+ }
+
+ void setParticleSize(qreal arg)
+ {
+ if (m_particleSize != arg) {
+ m_particleSize = arg;
+ emit particleSizeChanged(arg);
+ }
+ }
+
+ void setParticleEndSize(qreal arg)
+ {
+ if (m_particleEndSize != arg) {
+ m_particleEndSize = arg;
+ emit particleEndSizeChanged(arg);
+ }
+ }
+
+ void setParticleSizeVariation(qreal arg)
+ {
+ if (m_particleSizeVariation != arg) {
+ m_particleSizeVariation = arg;
+ emit particleSizeVariationChanged(arg);
+ }
+ }
+
+ void setSpeed(QQuickDirection * arg)
+ {
+ if (m_speed != arg) {
+ m_speed = arg;
+ emit speedChanged(arg);
+ }
+ }
+
+ void setAcceleration(QQuickDirection * arg)
+ {
+ if (m_acceleration != arg) {
+ m_acceleration = arg;
+ emit accelerationChanged(arg);
+ }
+ }
+
+ void setMaxParticleCount(int arg);
+
+ void setStartTime(int arg)
+ {
+ if (m_startTime != arg) {
+ m_startTime = arg;
+ emit startTimeChanged(arg);
+ }
+ }
+
+ virtual void reset();
+public:
+ int particleCount() const;
+
+ QQuickParticleExtruder* extruder() const
+ {
+ return m_extruder;
+ }
+
+ qreal particleSize() const
+ {
+ return m_particleSize;
+ }
+
+ qreal particleEndSize() const
+ {
+ return m_particleEndSize;
+ }
+
+ qreal particleSizeVariation() const
+ {
+ return m_particleSizeVariation;
+ }
+
+ QQuickDirection * speed() const
+ {
+ return m_speed;
+ }
+
+ QQuickDirection * acceleration() const
+ {
+ return m_acceleration;
+ }
+
+ int maxParticleCount() const
+ {
+ return m_maxParticleCount;
+ }
+
+ int startTime() const
+ {
+ return m_startTime;
+ }
+
+protected:
+ qreal m_particlesPerSecond;
+ int m_particleDuration;
+ int m_particleDurationVariation;
+ bool m_enabled;
+ QQuickParticleSystem* m_system;
+ QString m_group;
+ QQuickParticleExtruder* m_extruder;
+ QQuickParticleExtruder* m_defaultExtruder;
+ QQuickParticleExtruder* effectiveExtruder();
+ QQuickDirection * m_speed;
+ QQuickDirection * m_acceleration;
+ qreal m_particleSize;
+ qreal m_particleEndSize;
+ qreal m_particleSizeVariation;
+
+ qreal m_speedFromMovement;
+ int m_startTime;
+ bool m_overwrite;
+
+ int m_pulseLeft;
+ QList<QPair<int, QPointF > > m_burstQueue;
+ int m_maxParticleCount;
+
+ //Used in default implementation, but might be useful
+ qreal m_speed_from_movement;
+
+ int m_emitCap;
+ bool m_reset_last;
+ qreal m_last_timestamp;
+ qreal m_last_emission;
+
+ QPointF m_last_emitter;
+ QPointF m_last_last_emitter;
+ QPointF m_last_last_last_emitter;
+
+ bool isEmitConnected();
+private:
+ QQuickDirection m_nullVector;
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // PARTICLEEMITTER_H
diff --git a/src/particles/qquickparticleextruder.cpp b/src/particles/qquickparticleextruder.cpp
new file mode 100644
index 0000000000..279f7c05f3
--- /dev/null
+++ b/src/particles/qquickparticleextruder.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticleextruder_p.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass Shape QQuickParticleExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \brief The Shape element allows you to specify an area for affectors and emitter.
+
+ The base class is just a rectangle.
+*/
+
+QQuickParticleExtruder::QQuickParticleExtruder(QObject *parent) :
+ QObject(parent)
+{
+}
+
+QPointF QQuickParticleExtruder::extrude(const QRectF &rect)
+{
+ return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(),
+ ((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
+}
+
+bool QQuickParticleExtruder::contains(const QRectF &bounds, const QPointF &point)
+{
+ return bounds.contains(point);
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickparticleextruder_p.h b/src/particles/qquickparticleextruder_p.h
new file mode 100644
index 0000000000..e24950e4e8
--- /dev/null
+++ b/src/particles/qquickparticleextruder_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLEEXTRUDER_H
+#define PARTICLEEXTRUDER_H
+
+#include <QObject>
+#include <QRectF>
+#include <QPointF>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleExtruder : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickParticleExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ virtual bool contains(const QRectF &bounds, const QPointF &point);
+
+signals:
+public slots:
+protected:
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // PARTICLEEXTRUDER_H
diff --git a/src/particles/qquickparticlegroup.cpp b/src/particles/qquickparticlegroup.cpp
new file mode 100644
index 0000000000..9a165074a1
--- /dev/null
+++ b/src/particles/qquickparticlegroup.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticlegroup_p.h"
+
+/*!
+ \qmlclass ParticleGroup QQuickParticleGroup
+ \inqmlmodule QtQuick.Particles 2
+ \brief ParticleGroup elements allow you to set attributes on a logical particle group.
+
+ This element allows you to set timed transitions on particle groups.
+
+ You can also use this element to group particle system elements related to the logical
+ particle group. Emitters, Affectors and Painters set as direct children of a ParticleGroup
+ will automatically apply to that logical particle group. TrailEmitters will automatically follow
+ the group.
+
+ If a ParticleGroup element is not defined for a group, the group will function normally as if
+ none of the transition properties were set.
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::system
+ This is the system which will contain the group.
+
+ If the ParticleGroup is a direct child of a ParticleSystem, it will automatically be associated with it.
+*/
+/*!
+ \qmlproperty string QtQuick.Particles2::ParticleGroup::name
+ This is the name of the particle group, and how it is generally referred to by other elements.
+
+ If elements refer to a name which does not have an explicit ParticleGroup created, it will
+ work normally (with no transitions specified for the group). If you do not need to assign
+ duration based transitions to a group, you do not need to create a ParticleGroup with that name (although you may).
+*/
+/*!
+ \qmlproperty int QtQuick.Particles2::ParticleGroup::duration
+ The time in milliseconds before the group will attempt to transition.
+
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::durationVariation
+ The maximum number of milliseconds that the duration of the transition cycle varies per particle in the group.
+
+ Default value is zero.
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::to
+ The weighted list of transitions valid for this group.
+
+ If the chosen transition stays in this group, another duration (+/- up to durationVariation)
+ milliseconds will occur before another transition is attempted.
+*/
+
+QQuickParticleGroup::QQuickParticleGroup(QObject* parent)
+ : QQuickStochasticState(parent)
+ , m_system(0)
+{
+
+}
+
+void delayedRedirect(QQmlListProperty<QObject> *prop, QObject *value)
+{
+ QQuickParticleGroup* pg = qobject_cast<QQuickParticleGroup*>(prop->object);
+ if (pg)
+ pg->delayRedirect(value);
+}
+
+QQmlListProperty<QObject> QQuickParticleGroup::particleChildren()
+{
+ QQuickParticleSystem* system = qobject_cast<QQuickParticleSystem*>(parent());
+ if (system)
+ return QQmlListProperty<QObject>(this, 0, &QQuickParticleSystem::statePropertyRedirect);
+ else
+ return QQmlListProperty<QObject>(this, 0, &delayedRedirect);
+}
+
+void QQuickParticleGroup::setSystem(QQuickParticleSystem* arg)
+{
+ if (m_system != arg) {
+ m_system = arg;
+ m_system->registerParticleGroup(this);
+ performDelayedRedirects();
+ emit systemChanged(arg);
+ }
+}
+
+void QQuickParticleGroup::delayRedirect(QObject *obj)
+{
+ m_delayedRedirects << obj;
+}
+
+void QQuickParticleGroup::performDelayedRedirects()
+{
+ if (!m_system)
+ return;
+ foreach (QObject* obj, m_delayedRedirects)
+ m_system->stateRedirect(this, m_system, obj);
+
+ m_delayedRedirects.clear();
+}
+
+void QQuickParticleGroup::componentComplete(){
+ if (!m_system && qobject_cast<QQuickParticleSystem*>(parent()))
+ setSystem(qobject_cast<QQuickParticleSystem*>(parent()));
+}
diff --git a/src/particles/qquickparticlegroup_p.h b/src/particles/qquickparticlegroup_p.h
new file mode 100644
index 0000000000..8889187558
--- /dev/null
+++ b/src/particles/qquickparticlegroup_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QQuickPARTICLEGROUP
+#define QQuickPARTICLEGROUP
+#include <private/qquickspriteengine_p.h>
+#include "qquickparticlesystem_p.h"
+#include "qqmlparserstatus.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleGroup : public QQuickStochasticState, public QQmlParserStatus
+{
+ Q_OBJECT
+ //### Would setting limits per group be useful? Or clutter the API?
+ //Q_PROPERTY(int maximumAlive READ maximumAlive WRITE setMaximumAlive NOTIFY maximumAliveChanged)
+
+ Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+
+ //Intercept children requests and assign to the group & system
+ Q_PROPERTY(QQmlListProperty<QObject> particleChildren READ particleChildren DESIGNABLE false)//### Hidden property for in-state system definitions - ought not to be used in actual "Sprite" states
+ Q_CLASSINFO("DefaultProperty", "particleChildren")
+ Q_INTERFACES(QQmlParserStatus)
+
+public:
+ explicit QQuickParticleGroup(QObject* parent = 0);
+
+ QQmlListProperty<QObject> particleChildren();
+
+ int maximumAlive() const
+ {
+ return m_maximumAlive;
+ }
+
+ QQuickParticleSystem* system() const
+ {
+ return m_system;
+ }
+
+public slots:
+
+ void setMaximumAlive(int arg)
+ {
+ if (m_maximumAlive != arg) {
+ m_maximumAlive = arg;
+ emit maximumAliveChanged(arg);
+ }
+ }
+
+ void setSystem(QQuickParticleSystem* arg);
+
+ void delayRedirect(QObject* obj);
+
+signals:
+
+ void maximumAliveChanged(int arg);
+
+ void systemChanged(QQuickParticleSystem* arg);
+
+protected:
+ virtual void componentComplete();
+ virtual void classBegin(){;}
+
+private:
+
+ void performDelayedRedirects();
+
+ int m_maximumAlive;
+ QQuickParticleSystem* m_system;
+ QList<QObject*> m_delayedRedirects;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/particles/qquickparticlepainter.cpp b/src/particles/qquickparticlepainter.cpp
new file mode 100644
index 0000000000..e490b70240
--- /dev/null
+++ b/src/particles/qquickparticlepainter.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticlepainter_p.h"
+#include <QQuickCanvas>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass ParticlePainter QQuickParticlePainter
+ \inqmlmodule QtQuick.Particles 2
+ \inherits ParticlePainter
+ \brief ParticlePainter elements allow you to specify how to paint particles.
+
+ The default implementation paints nothing. See the subclasses if you want to
+ paint something visible.
+
+*/
+/*!
+ \qmlproperty ParticleSystem QtQuick.Particles2::ParticlePainter::system
+ This is the system whose particles can be painted by the element.
+ If the ParticlePainter is a direct child of a ParticleSystem, it will automatically be associated with it.
+*/
+/*!
+ \qmlproperty list<string> QtQuick.Particles2::ParticlePainter::groups
+ Which logical particle groups will be painted.
+
+ If empty, it will paint the default particle group ("").
+*/
+QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent) :
+ QQuickItem(parent),
+ m_system(0), m_count(0), m_pleaseReset(true), m_canvas(0)
+{
+}
+
+void QQuickParticlePainter::itemChange(ItemChange change, const ItemChangeData &data)
+{
+ if (change == QQuickItem::ItemSceneChange) {
+ if (m_canvas)
+ disconnect(m_canvas, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated()));
+ m_canvas = data.canvas;
+ if (m_canvas)
+ connect(m_canvas, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated()), Qt::DirectConnection);
+
+ }
+}
+
+void QQuickParticlePainter::componentComplete()
+{
+ if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem()))
+ setSystem(qobject_cast<QQuickParticleSystem*>(parentItem()));
+ QQuickItem::componentComplete();
+}
+
+
+void QQuickParticlePainter::setSystem(QQuickParticleSystem *arg)
+{
+ if (m_system != arg) {
+ m_system = arg;
+ if (m_system){
+ m_system->registerParticlePainter(this);
+ reset();
+ }
+ emit systemChanged(arg);
+ }
+}
+
+void QQuickParticlePainter::load(QQuickParticleData* d)
+{
+ initialize(d->group, d->index);
+ if (m_pleaseReset)
+ return;
+ m_pendingCommits << qMakePair<int, int>(d->group, d->index);
+}
+
+void QQuickParticlePainter::reload(QQuickParticleData* d)
+{
+ if (m_pleaseReset)
+ return;
+ m_pendingCommits << qMakePair<int, int>(d->group, d->index);
+}
+
+void QQuickParticlePainter::reset()
+{
+ m_pendingCommits.clear();
+ m_pleaseReset = true;
+}
+
+void QQuickParticlePainter::setCount(int c)//### TODO: some resizeing so that particles can reallocate on size change instead of recreate
+{
+ Q_ASSERT(c >= 0); //XXX
+ if (c == m_count)
+ return;
+ m_count = c;
+ emit countChanged();
+ reset();
+}
+
+int QQuickParticlePainter::count()
+{
+ return m_count;
+}
+
+void QQuickParticlePainter::calcSystemOffset(bool resetPending)
+{
+ if (!m_system || !parentItem())
+ return;
+ QPointF lastOffset = m_systemOffset;
+ m_systemOffset = -1 * this->mapFromItem(m_system, QPointF(0.0, 0.0));
+ if (lastOffset != m_systemOffset && !resetPending){
+ //Reload all particles//TODO: Necessary?
+ foreach (const QString &g, m_groups){
+ int gId = m_system->groupIds[g];
+ foreach (QQuickParticleData* d, m_system->groupData[gId]->data)
+ reload(d);
+ }
+ }
+}
+typedef QPair<int,int> intPair;
+void QQuickParticlePainter::performPendingCommits()
+{
+ calcSystemOffset();
+ foreach (intPair p, m_pendingCommits)
+ commit(p.first, p.second);
+ m_pendingCommits.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickparticlepainter_p.h b/src/particles/qquickparticlepainter_p.h
new file mode 100644
index 0000000000..1ae4625856
--- /dev/null
+++ b/src/particles/qquickparticlepainter_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLE_H
+#define PARTICLE_H
+
+#include <QObject>
+#include <QDebug>
+#include <QPair>
+#include "qquickparticlesystem_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticlePainter : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
+ Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
+
+public:
+ explicit QQuickParticlePainter(QQuickItem *parent = 0);
+ //Data Interface to system
+ void load(QQuickParticleData*);
+ void reload(QQuickParticleData*);
+ void setCount(int c);
+ int count();
+ void performPendingCommits();//Called from updatePaintNode
+ QQuickParticleSystem* system() const
+ {
+ return m_system;
+ }
+
+
+ QStringList groups() const
+ {
+ return m_groups;
+ }
+
+ void itemChange(ItemChange, const ItemChangeData &);
+
+signals:
+ void countChanged();
+ void systemChanged(QQuickParticleSystem* arg);
+
+ void groupsChanged(QStringList arg);
+
+public slots:
+ void setSystem(QQuickParticleSystem* arg);
+
+ void setGroups(QStringList arg)
+ {
+ if (m_groups != arg) {
+ m_groups = arg;
+ //Note: The system watches this as it has to recalc things when groups change. It will request a reset if necessary
+ emit groupsChanged(arg);
+ }
+ }
+
+ void calcSystemOffset(bool resetPending = false);
+
+private slots:
+ virtual void sceneGraphInvalidated() {}
+
+protected:
+ /* Reset resets all your internal data structures. But anything attached to a particle should
+ be in attached data. So reset + reloads should have no visible effect.
+ ###Hunt down all cases where we do a complete reset for convenience and be more targeted
+ */
+ virtual void reset();
+
+ virtual void componentComplete();
+ virtual void initialize(int gIdx, int pIdx){//Called from main thread
+ Q_UNUSED(gIdx);
+ Q_UNUSED(pIdx);
+ }
+ virtual void commit(int gIdx, int pIdx){//Called in Render Thread
+ //###If you need to do something on size changed, check m_data size in this? Or we reset you every time?
+ Q_UNUSED(gIdx);
+ Q_UNUSED(pIdx);
+ }
+
+ QQuickParticleSystem* m_system;
+ friend class QQuickParticleSystem;
+ int m_count;
+ bool m_pleaseReset;//Used by subclasses, but it's a nice optimization to know when stuff isn't going to matter.
+ QStringList m_groups;
+ QPointF m_systemOffset;
+
+ QQuickCanvas *m_canvas;
+
+private:
+ QSet<QPair<int,int> > m_pendingCommits;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // PARTICLE_H
diff --git a/src/particles/qquickparticlesmodule.cpp b/src/particles/qquickparticlesmodule.cpp
new file mode 100644
index 0000000000..e83cde3cd1
--- /dev/null
+++ b/src/particles/qquickparticlesmodule.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickangledirection_p.h"
+#include "qquickcustomparticle_p.h"
+#include "qquickellipseextruder_p.h"
+#include "qquicktrailemitter_p.h"
+#include "qquickfriction_p.h"
+#include "qquickgravity_p.h"
+#include "qquickimageparticle_p.h"
+#include "qquickitemparticle_p.h"
+#include "qquickage_p.h"
+#include "qquicklineextruder_p.h"
+#include "qquickmaskextruder_p.h"
+#include "qquickparticleaffector_p.h"
+#include "qquickparticleemitter_p.h"
+#include "qquickparticleextruder_p.h"
+#include "qquickparticlepainter_p.h"
+#include "qquickparticlesmodule_p.h"
+#include "qquickparticlesystem_p.h"
+#include "qquickpointattractor_p.h"
+#include "qquickpointdirection_p.h"
+#include "qquickspritegoal_p.h"
+#include "qquickdirection_p.h"
+#include "qquicktargetdirection_p.h"
+#include "qquickturbulence_p.h"
+#include "qquickwander_p.h"
+#include "qquickcumulativedirection_p.h"
+#include "qquickcustomaffector_p.h"
+#include "qquickrectangleextruder_p.h"
+#include "qquickparticlegroup_p.h"
+#include "qquickgroupgoal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QQuickParticlesModule::defineModule()
+{
+ const char* uri = "QtQuick.Particles";
+
+ qmlRegisterType<QQuickParticleSystem>(uri, 2, 0, "ParticleSystem");
+ qmlRegisterType<QQuickParticleGroup>(uri, 2, 0, "ParticleGroup");
+
+ qmlRegisterType<QQuickImageParticle>(uri, 2, 0, "ImageParticle");
+ qmlRegisterType<QQuickCustomParticle>(uri, 2, 0, "CustomParticle");
+ qmlRegisterType<QQuickItemParticle>(uri, 2, 0, "ItemParticle");
+
+ qmlRegisterType<QQuickParticleEmitter>(uri, 2, 0, "Emitter");
+ qmlRegisterType<QQuickTrailEmitter>(uri, 2, 0, "TrailEmitter");
+
+ qmlRegisterType<QQuickEllipseExtruder>(uri, 2, 0, "EllipseShape");
+ qmlRegisterType<QQuickRectangleExtruder>(uri, 2, 0, "RectangleShape");
+ qmlRegisterType<QQuickLineExtruder>(uri, 2, 0, "LineShape");
+ qmlRegisterType<QQuickMaskExtruder>(uri, 2, 0, "MaskShape");
+
+ qmlRegisterType<QQuickPointDirection>(uri, 2, 0, "PointDirection");
+ qmlRegisterType<QQuickAngleDirection>(uri, 2, 0, "AngleDirection");
+ qmlRegisterType<QQuickTargetDirection>(uri, 2, 0, "TargetDirection");
+ qmlRegisterType<QQuickCumulativeDirection>(uri, 2, 0, "CumulativeDirection");
+
+ qmlRegisterType<QQuickCustomAffector>(uri, 2, 0, "Affector");
+ qmlRegisterType<QQuickWanderAffector>(uri, 2, 0, "Wander");
+ qmlRegisterType<QQuickFrictionAffector>(uri, 2, 0, "Friction");
+ qmlRegisterType<QQuickAttractorAffector>(uri, 2, 0, "Attractor");
+ qmlRegisterType<QQuickGravityAffector>(uri, 2, 0, "Gravity");
+ qmlRegisterType<QQuickAgeAffector>(uri, 2, 0, "Age");
+ qmlRegisterType<QQuickSpriteGoalAffector>(uri, 2, 0, "SpriteGoal");
+ qmlRegisterType<QQuickGroupGoalAffector>(uri, 2, 0, "GroupGoal");
+ qmlRegisterType<QQuickTurbulenceAffector>(uri, 2, 0 , "Turbulence");
+
+ //Exposed just for completeness
+ qmlRegisterUncreatableType<QQuickParticleAffector>(uri, 2, 0, "ParticleAffector",
+ QStringLiteral("Abstract type. Use one of the inheriting types instead."));
+ qmlRegisterUncreatableType<QQuickParticlePainter>(uri, 2, 0, "ParticlePainter",
+ QStringLiteral("Abstract type. Use one of the inheriting types instead."));
+ qmlRegisterUncreatableType<QQuickParticleExtruder>(uri, 2, 0, "ParticleExtruder",
+ QStringLiteral("Abstract type. Use one of the inheriting types instead."));
+ qmlRegisterUncreatableType<QQuickDirection>(uri, 2, 0, "NullVector",
+ QStringLiteral("Abstract type. Use one of the inheriting types instead."));
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/particles/qquickparticlesmodule_p.h b/src/particles/qquickparticlesmodule_p.h
new file mode 100644
index 0000000000..87e99fa6bb
--- /dev/null
+++ b/src/particles/qquickparticlesmodule_p.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPARTICLESMODULE_H
+#define QQUICKPARTICLESMODULE_H
+
+#include <private/qtquickparticlesglobal_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticlesModule
+{
+public:
+ static void defineModule();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QQUICKPARTICLESMODULE_H
diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp
new file mode 100644
index 0000000000..4fd6108f2f
--- /dev/null
+++ b/src/particles/qquickparticlesystem.cpp
@@ -0,0 +1,1164 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickparticlesystem_p.h"
+#include <QtQuick/qsgnode.h>
+#include "qquickparticleemitter_p.h"
+#include "qquickparticleaffector_p.h"
+#include "qquickparticlepainter_p.h"
+#include <private/qquickspriteengine_p.h>
+#include <private/qquicksprite_p.h>
+#include "qquickv8particledata_p.h"
+#include "qquickparticlegroup_p.h"
+
+#include "qquicktrailemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
+#include <private/qqmlengine_p.h>
+#include <private/qqmlglobal_p.h>
+#include <cmath>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+//###Switch to define later, for now user-friendly (no compilation) debugging is worth it
+DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG)
+
+
+/* \internal ParticleSystem internals documentation
+
+ Affectors, Painters, Emitters and Groups all register themselves on construction as a callback
+ from their setSystem (or componentComplete if they have a system from a parent).
+
+ Particle data is stored by group, They have a group index (used by the particle system almost
+ everywhere) and a global index (used by the Stochastic state engine powering stochastic group
+ transitions). Each group has a recycling list/heap that stores the particle data.
+
+ The recycling list/heap is a heap of particle data sorted by when they're expected to die. If
+ they die prematurely then they are marked as reusable (and will probably still be alive when
+ they exit the heap). If they have their life extended, then they aren't dead when expected.
+ If this happens, they go back in the heap with the new estimate. If they have died on schedule,
+ then the indexes are marked as reusable. If no indexes are reusable when new particles are
+ requested, then the list is extended. This relatively complex datastructure is because memory
+ allocation and deallocation on this scale proved to be a significant performance cost. In order
+ to reuse the indexes validly (even when particles can have their life extended or cut short
+ dynamically, or particle counts grow) this seemed to be the most efficient option for keeping
+ track of which indices could be reused.
+
+ When a new particle is emitted, the emitter gets a new datum from the group (through the
+ system), and sets properties on it. Then it's passed back to the group briefly so that it can
+ now guess when the particle will die. Then the painters get a change to initialize properties
+ as well, since particle data includes shared data from painters as well as logical particle
+ data.
+
+ Every animation advance, the simulation advances by running all emitters for the elapsed
+ duration, then running all affectors, then telling all particle painters to update changed
+ particles. The ParticlePainter superclass stores these changes, and they are implemented
+ when the painter is called to paint in the render thread.
+
+ Particle group changes move the particle from one group to another by killing the old particle
+ and then creating a new one with the same data in the new group.
+
+ Note that currently groups only grow. Given that data is stored in vectors, it is non-trivial
+ to pluck out the unused indexes when the count goes down. Given the dynamic nature of the
+ system, it is difficult to tell if those unused data instances will be used again. Still,
+ some form of garbage collection is on the long term plan.
+*/
+
+/*!
+ \qmlclass ParticleSystem QQuickParticleSystem
+ \inqmlmodule QtQuick.Particles 2
+ \brief The ParticleSystem brings together ParticlePainter, Emitter and Affector elements.
+
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::ParticleSystem::running
+
+ If running is set to false, the particle system will stop the simulation. All particles
+ will be destroyed when the system is set to running again.
+
+ It can also be controlled with the start() and stop() methods.
+*/
+
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::ParticleSystem::paused
+
+ If paused is set to true, the particle system will not advance the simulation. When
+ paused is set to false again, the simulation will resume from the same point it was
+ paused.
+
+ The simulation will automatically pause if it detects that there are no live particles
+ left, and unpause when new live particles are added.
+
+ It can also be controlled with the pause() and resume() methods.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::ParticleSystem::empty
+
+ empty is set to true when there are no live particles left in the system.
+
+ You can use this to pause the system, keeping it from spending any time updating,
+ but you will need to resume it in order for additional particles to be generated
+ by the system.
+
+ To kill all the particles in the system, use a Kill affector.
+*/
+
+/*!
+ \qmlproperty list<Sprite> QtQuick.Particles2::ParticleSystem::particleStates
+
+ You can define a sub-set of particle groups in this property in order to provide them
+ with stochastic state transitions.
+
+ Each QtQuick2::Sprite in this list is interpreted as corresponding to the particle group
+ with ths same name. Any transitions defined in these sprites will take effect on the particle
+ groups as well. Additionally TrailEmitters, Affectors and ParticlePainters definined
+ inside one of these sprites are automatically associated with the corresponding particle group.
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::pause
+
+ Pauses the simulation if it is running.
+
+ \sa resume, paused
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::resume
+
+ Resumes the simulation if it is paused.
+
+ \sa pause, paused
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::start
+
+ Starts the simulation if it has not already running.
+
+ \sa stop, restart, running
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::stop
+
+ Stops the simulation if it is running.
+
+ \sa start, restart, running
+*/
+
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::restart
+
+ Stops the simulation if it is running, and then starts it.
+
+ \sa stop, restart, running
+*/
+/*!
+ \qmlmethod void QtQuick.Particles2::ParticleSystem::reset
+
+ Discards all currently existing particles.
+
+*/
+const qreal EPSILON = 0.001;
+//Utility functions for when within 1ms is close enough
+bool timeEqualOrGreater(qreal a, qreal b)
+{
+ return (a+EPSILON >= b);
+}
+
+bool timeLess(qreal a, qreal b)
+{
+ return (a-EPSILON < b);
+}
+
+bool timeEqual(qreal a, qreal b)
+{
+ return (a+EPSILON > b) && (a-EPSILON < b);
+}
+
+int roundedTime(qreal a)
+{// in ms
+ return (int)qRound(a*1000.0);
+}
+
+QQuickParticleDataHeap::QQuickParticleDataHeap()
+ : m_data(0)
+{
+ m_data.reserve(1000);
+ clear();
+}
+
+void QQuickParticleDataHeap::grow() //###Consider automatic growth vs resize() calls from GroupData
+{
+ m_data.resize(1 << ++m_size);
+}
+
+void QQuickParticleDataHeap::insert(QQuickParticleData* data)
+{
+ insertTimed(data, roundedTime(data->t + data->lifeSpan));
+}
+
+void QQuickParticleDataHeap::insertTimed(QQuickParticleData* data, int time)
+{
+ //TODO: Optimize 0 lifespan (or already dead) case
+ if (m_lookups.contains(time)) {
+ m_data[m_lookups[time]].data << data;
+ return;
+ }
+ if (m_end == (1 << m_size))
+ grow();
+ m_data[m_end].time = time;
+ m_data[m_end].data.clear();
+ m_data[m_end].data.insert(data);
+ m_lookups.insert(time, m_end);
+ bubbleUp(m_end++);
+}
+
+int QQuickParticleDataHeap::top()
+{
+ if (m_end == 0)
+ return 1 << 30;
+ return m_data[0].time;
+}
+
+QSet<QQuickParticleData*> QQuickParticleDataHeap::pop()
+{
+ if (!m_end)
+ return QSet<QQuickParticleData*> ();
+ QSet<QQuickParticleData*> ret = m_data[0].data;
+ m_lookups.remove(m_data[0].time);
+ if (m_end == 1) {
+ --m_end;
+ } else {
+ m_data[0] = m_data[--m_end];
+ bubbleDown(0);
+ }
+ return ret;
+}
+
+void QQuickParticleDataHeap::clear()
+{
+ m_size = 0;
+ m_end = 0;
+ //m_size is in powers of two. So to start at 0 we have one allocated
+ m_data.resize(1);
+ m_lookups.clear();
+}
+
+bool QQuickParticleDataHeap::contains(QQuickParticleData* d)
+{
+ for (int i=0; i<m_end; i++)
+ if (m_data[i].data.contains(d))
+ return true;
+ return false;
+}
+
+void QQuickParticleDataHeap::swap(int a, int b)
+{
+ m_tmp = m_data[a];
+ m_data[a] = m_data[b];
+ m_data[b] = m_tmp;
+ m_lookups[m_data[a].time] = a;
+ m_lookups[m_data[b].time] = b;
+}
+
+void QQuickParticleDataHeap::bubbleUp(int idx)//tends to be called once
+{
+ if (!idx)
+ return;
+ int parent = (idx-1)/2;
+ if (m_data[idx].time < m_data[parent].time) {
+ swap(idx, parent);
+ bubbleUp(parent);
+ }
+}
+
+void QQuickParticleDataHeap::bubbleDown(int idx)//tends to be called log n times
+{
+ int left = idx*2 + 1;
+ if (left >= m_end)
+ return;
+ int lesser = left;
+ int right = idx*2 + 2;
+ if (right < m_end) {
+ if (m_data[left].time > m_data[right].time)
+ lesser = right;
+ }
+ if (m_data[idx].time > m_data[lesser].time) {
+ swap(idx, lesser);
+ bubbleDown(lesser);
+ }
+}
+
+QQuickParticleGroupData::QQuickParticleGroupData(int id, QQuickParticleSystem* sys):index(id),m_size(0),m_system(sys)
+{
+ initList();
+}
+
+QQuickParticleGroupData::~QQuickParticleGroupData()
+{
+ foreach (QQuickParticleData* d, data)
+ delete d;
+}
+
+int QQuickParticleGroupData::size()
+{
+ return m_size;
+}
+
+QString QQuickParticleGroupData::name()//### Worth caching as well?
+{
+ return m_system->groupIds.key(index);
+}
+
+void QQuickParticleGroupData::setSize(int newSize)
+{
+ if (newSize == m_size)
+ return;
+ Q_ASSERT(newSize > m_size);//XXX allow shrinking
+ data.resize(newSize);
+ for (int i=m_size; i<newSize; i++) {
+ data[i] = new QQuickParticleData(m_system);
+ data[i]->group = index;
+ data[i]->index = i;
+ reusableIndexes << i;
+ }
+ int delta = newSize - m_size;
+ m_size = newSize;
+ foreach (QQuickParticlePainter* p, painters)
+ p->setCount(p->count() + delta);
+}
+
+void QQuickParticleGroupData::initList()
+{
+ dataHeap.clear();
+}
+
+void QQuickParticleGroupData::kill(QQuickParticleData* d)
+{
+ Q_ASSERT(d->group == index);
+ d->lifeSpan = 0;//Kill off
+ foreach (QQuickParticlePainter* p, painters)
+ p->reload(d);
+ reusableIndexes << d->index;
+}
+
+QQuickParticleData* QQuickParticleGroupData::newDatum(bool respectsLimits)
+{
+ //recycle();//Extra recycler round to be sure?
+
+ while (!reusableIndexes.empty()) {
+ int idx = *(reusableIndexes.begin());
+ reusableIndexes.remove(idx);
+ if (data[idx]->stillAlive()) {// ### This means resurrection of 'dead' particles. Is that allowed?
+ prepareRecycler(data[idx]);
+ continue;
+ }
+ return data[idx];
+ }
+ if (respectsLimits)
+ return 0;
+
+ int oldSize = m_size;
+ setSize(oldSize + 10);//###+1,10%,+10? Choose something non-arbitrarily
+ reusableIndexes.remove(oldSize);
+ return data[oldSize];
+}
+
+bool QQuickParticleGroupData::recycle()
+{
+ while (dataHeap.top() <= m_system->timeInt) {
+ foreach (QQuickParticleData* datum, dataHeap.pop()) {
+ if (!datum->stillAlive()) {
+ reusableIndexes << datum->index;
+ } else {
+ prepareRecycler(datum); //ttl has been altered mid-way, put it back
+ }
+ }
+ }
+
+ //TODO: If the data is clear, gc (consider shrinking stack size)?
+ return reusableIndexes.count() == m_size;
+}
+
+void QQuickParticleGroupData::prepareRecycler(QQuickParticleData* d)
+{
+ if (d->lifeSpan*1000 < m_system->maxLife) {
+ dataHeap.insert(d);
+ } else {
+ while ((roundedTime(d->t) + 2*m_system->maxLife/3) <= m_system->timeInt)
+ d->extendLife(m_system->maxLife/3000.0);
+ dataHeap.insertTimed(d, roundedTime(d->t) + 2*m_system->maxLife/3);
+ }
+}
+
+QQuickParticleData::QQuickParticleData(QQuickParticleSystem* sys)
+ : group(0)
+ , e(0)
+ , system(sys)
+ , index(0)
+ , systemIndex(-1)
+ , colorOwner(0)
+ , rotationOwner(0)
+ , deformationOwner(0)
+ , animationOwner(0)
+ , v8Datum(0)
+{
+ x = 0;
+ y = 0;
+ t = -1;
+ lifeSpan = 0;
+ size = 0;
+ endSize = 0;
+ vx = 0;
+ vy = 0;
+ ax = 0;
+ ay = 0;
+ xx = 1;
+ xy = 0;
+ yx = 0;
+ yy = 1;
+ rotation = 0;
+ rotationSpeed = 0;
+ autoRotate = 0;
+ animIdx = 0;
+ frameDuration = 1;
+ frameAt = -1;
+ frameCount = 1;
+ animT = -1;
+ animX = 0;
+ animY = 0;
+ animWidth = 1;
+ animHeight = 1;
+ color.r = 255;
+ color.g = 255;
+ color.b = 255;
+ color.a = 255;
+ r = 0;
+ delegate = 0;
+ modelIndex = -1;
+}
+
+QQuickParticleData::~QQuickParticleData()
+{
+ delete v8Datum;
+}
+
+void QQuickParticleData::clone(const QQuickParticleData& other)
+{
+ x = other.x;
+ y = other.y;
+ t = other.t;
+ lifeSpan = other.lifeSpan;
+ size = other.size;
+ endSize = other.endSize;
+ vx = other.vx;
+ vy = other.vy;
+ ax = other.ax;
+ ay = other.ay;
+ xx = other.xx;
+ xy = other.xy;
+ yx = other.yx;
+ yy = other.yy;
+ rotation = other.rotation;
+ rotationSpeed = other.rotationSpeed;
+ autoRotate = other.autoRotate;
+ animIdx = other.animIdx;
+ frameDuration = other.frameDuration;
+ frameCount = other.frameCount;
+ animT = other.animT;
+ animX = other.animX;
+ animY = other.animY;
+ animWidth = other.animWidth;
+ animHeight = other.animHeight;
+ color.r = other.color.r;
+ color.g = other.color.g;
+ color.b = other.color.b;
+ color.a = other.color.a;
+ r = other.r;
+ delegate = other.delegate;
+ modelIndex = other.modelIndex;
+
+ colorOwner = other.colorOwner;
+ rotationOwner = other.rotationOwner;
+ deformationOwner = other.deformationOwner;
+ animationOwner = other.animationOwner;
+}
+
+QQmlV8Handle QQuickParticleData::v8Value()
+{
+ if (!v8Datum)
+ v8Datum = new QQuickV8ParticleData(QQmlEnginePrivate::getV8Engine(qmlEngine(system)), this);
+ return v8Datum->v8Value();
+}
+//sets the x accleration without affecting the instantaneous x velocity or position
+void QQuickParticleData::setInstantaneousAX(qreal ax)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ qreal vx = (this->vx + t*this->ax) - t*ax;
+ qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
+ qreal x = ex - t*vx - 0.5 * t*t*ax;
+
+ this->ax = ax;
+ this->vx = vx;
+ this->x = x;
+}
+
+//sets the x velocity without affecting the instantaneous x postion
+void QQuickParticleData::setInstantaneousVX(qreal vx)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ qreal evx = vx - t*this->ax;
+ qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
+ qreal x = ex - t*evx - 0.5 * t*t*this->ax;
+
+ this->vx = evx;
+ this->x = x;
+}
+
+//sets the instantaneous x postion
+void QQuickParticleData::setInstantaneousX(qreal x)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ this->x = x - t*this->vx - 0.5 * t*t*this->ax;
+}
+
+//sets the y accleration without affecting the instantaneous y velocity or position
+void QQuickParticleData::setInstantaneousAY(qreal ay)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ qreal vy = (this->vy + t*this->ay) - t*ay;
+ qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
+ qreal y = ey - t*vy - 0.5 * t*t*ay;
+
+ this->ay = ay;
+ this->vy = vy;
+ this->y = y;
+}
+
+//sets the y velocity without affecting the instantaneous y position
+void QQuickParticleData::setInstantaneousVY(qreal vy)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ qreal evy = vy - t*this->ay;
+ qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
+ qreal y = ey - t*evy - 0.5 * t*t*this->ay;
+
+ this->vy = evy;
+ this->y = y;
+}
+
+//sets the instantaneous Y position
+void QQuickParticleData::setInstantaneousY(qreal y)
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ this->y = y - t*this->vy - 0.5 * t*t*this->ay;
+}
+
+qreal QQuickParticleData::curX() const
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ return this->x + this->vx * t + 0.5 * this->ax * t * t;
+}
+
+qreal QQuickParticleData::curVX() const
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ return this->vx + t*this->ax;
+}
+
+qreal QQuickParticleData::curY() const
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ return y + vy * t + 0.5 * ay * t * t;
+}
+
+qreal QQuickParticleData::curVY() const
+{
+ qreal t = (system->timeInt / 1000.0) - this->t;
+ return vy + t*ay;
+}
+
+void QQuickParticleData::debugDump()
+{
+ qDebug() << "Particle" << systemIndex << group << "/" << index << stillAlive()
+ << "Pos: " << x << "," << y
+ << "Vel: " << vx << "," << vy
+ << "Acc: " << ax << "," << ay
+ << "Size: " << size << "," << endSize
+ << "Time: " << t << "," <<lifeSpan << ";" << (system->timeInt / 1000.0) ;
+}
+
+bool QQuickParticleData::stillAlive()
+{
+ if (!system)
+ return false;
+ return (t + lifeSpan - EPSILON) > ((qreal)system->timeInt/1000.0);
+}
+
+bool QQuickParticleData::alive()
+{
+ if (!system)
+ return false;
+ qreal st = ((qreal)system->timeInt/1000.0);
+ return (t + EPSILON) < st && (t + lifeSpan - EPSILON) > st;
+}
+
+float QQuickParticleData::curSize()
+{
+ if (!system || !lifeSpan)
+ return 0.0f;
+ return size + (endSize - size) * (1 - (lifeLeft() / lifeSpan));
+}
+
+float QQuickParticleData::lifeLeft()
+{
+ if (!system)
+ return 0.0f;
+ return (t + lifeSpan) - (system->timeInt/1000.0);
+}
+
+void QQuickParticleData::extendLife(float time)
+{
+ qreal newX = curX();
+ qreal newY = curY();
+ qreal newVX = curVX();
+ qreal newVY = curVY();
+
+ t += time;
+ animT += time;
+
+ qreal elapsed = (system->timeInt / 1000.0) - t;
+ qreal evy = newVY - elapsed*ay;
+ qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay;
+ qreal evx = newVX - elapsed*ax;
+ qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax;
+
+ x = ex;
+ vx = evx;
+ y = ey;
+ vy = evy;
+}
+
+QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) :
+ QQuickItem(parent),
+ stateEngine(0),
+ m_animation(0),
+ m_running(true),
+ initialized(0),
+ particleCount(0),
+ m_nextIndex(0),
+ m_componentComplete(false),
+ m_paused(false),
+ m_empty(true)
+{
+ connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
+ this, SLOT(loadPainter(QObject*)));
+
+ m_debugMode = qmlParticlesDebug();
+}
+
+QQuickParticleSystem::~QQuickParticleSystem()
+{
+ foreach (QQuickParticleGroupData* gd, groupData)
+ delete gd;
+}
+
+void QQuickParticleSystem::initGroups()
+{
+ m_reusableIndexes.clear();
+ m_nextIndex = 0;
+
+ qDeleteAll(groupData);
+ groupData.clear();
+ groupIds.clear();
+
+ QQuickParticleGroupData* gd = new QQuickParticleGroupData(0, this);//Default group
+ groupData.insert(0,gd);
+ groupIds.insert(QString(), 0);
+ m_nextGroupId = 1;
+}
+
+void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
+{
+ if (m_debugMode)
+ qDebug() << "Registering Painter" << p << "to" << this;
+ //TODO: a way to Unregister emitters, painters and affectors
+ m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking?
+ connect(p, SIGNAL(groupsChanged(QStringList)),
+ &m_painterMapper, SLOT(map()));
+ loadPainter(p);
+}
+
+void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
+{
+ if (m_debugMode)
+ qDebug() << "Registering Emitter" << e << "to" << this;
+ m_emitters << QPointer<QQuickParticleEmitter>(e);//###How to get them out?
+ connect(e, SIGNAL(particleCountChanged()),
+ this, SLOT(emittersChanged()));
+ connect(e, SIGNAL(groupChanged(QString)),
+ this, SLOT(emittersChanged()));
+ emittersChanged();
+ e->reset();//Start, so that starttime factors appropriately
+}
+
+void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
+{
+ if (m_debugMode)
+ qDebug() << "Registering Affector" << a << "to" << this;
+ m_affectors << QPointer<QQuickParticleAffector>(a);
+}
+
+void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
+{
+ if (m_debugMode)
+ qDebug() << "Registering Group" << g << "to" << this;
+ m_groups << QPointer<QQuickParticleGroup>(g);
+ createEngine();
+}
+
+void QQuickParticleSystem::setRunning(bool arg)
+{
+ if (m_running != arg) {
+ m_running = arg;
+ emit runningChanged(arg);
+ setPaused(false);
+ if (m_animation)//Not created until componentCompleted
+ m_running ? m_animation->start() : m_animation->stop();
+ reset();
+ }
+}
+
+void QQuickParticleSystem::setPaused(bool arg) {
+ if (m_paused != arg) {
+ m_paused = arg;
+ if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
+ m_paused ? m_animation->pause() : m_animation->resume();
+ if (!m_paused) {
+ foreach (QQuickParticlePainter *p, m_painters)
+ p->update();
+ }
+ emit pausedChanged(arg);
+ }
+}
+
+void QQuickParticleSystem::statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value)
+{
+ //Hooks up automatic state-associated stuff
+ QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
+ QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
+ if (!group || !sys || !value)
+ return;
+ stateRedirect(group, sys, value);
+}
+
+void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
+{
+ QStringList list;
+ list << group->name();
+ QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
+ if (a) {
+ a->setParentItem(sys);
+ a->setGroups(list);
+ a->setSystem(sys);
+ return;
+ }
+ QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
+ if (fe) {
+ fe->setParentItem(sys);
+ fe->setFollow(group->name());
+ fe->setSystem(sys);
+ return;
+ }
+ QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
+ if (e) {
+ e->setParentItem(sys);
+ e->setGroup(group->name());
+ e->setSystem(sys);
+ return;
+ }
+ QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
+ if (p) {
+ p->setParentItem(sys);
+ p->setGroups(list);
+ p->setSystem(sys);
+ return;
+ }
+ qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
+}
+
+void QQuickParticleSystem::componentComplete()
+
+{
+ QQuickItem::componentComplete();
+ m_componentComplete = true;
+ m_animation = new QQuickParticleSystemAnimation(this);
+ reset();//restarts animation as well
+}
+
+void QQuickParticleSystem::reset()
+{
+ if (!m_componentComplete)
+ return;
+
+ timeInt = 0;
+ //Clear guarded pointers which have been deleted
+ int cleared = 0;
+ cleared += m_emitters.removeAll(0);
+ cleared += m_painters.removeAll(0);
+ cleared += m_affectors.removeAll(0);
+
+ bySysIdx.resize(0);
+ initGroups();//Also clears all logical particles
+
+ if (!m_running)
+ return;
+
+ foreach (QQuickParticleEmitter* e, m_emitters)
+ e->reset();
+
+ emittersChanged();
+
+ foreach (QQuickParticlePainter *p, m_painters) {
+ loadPainter(p);
+ p->reset();
+ }
+
+ //### Do affectors need reset too?
+ if (m_animation) {//Animation is explicitly disabled in benchmarks
+ //reset restarts animation (if running)
+ if ((m_animation->state() == QAbstractAnimation::Running))
+ m_animation->stop();
+ m_animation->start();
+ if (m_paused)
+ m_animation->pause();
+ }
+
+ initialized = true;
+}
+
+
+void QQuickParticleSystem::loadPainter(QObject *p)
+{
+ if (!m_componentComplete || !p)
+ return;
+
+ QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p);
+ Q_ASSERT(painter);//XXX
+ foreach (QQuickParticleGroupData* sg, groupData)
+ sg->painters.remove(painter);
+ int particleCount = 0;
+ if (painter->groups().isEmpty()) {//Uses default particle
+ QStringList def;
+ def << QString();
+ painter->setGroups(def);
+ particleCount += groupData[0]->size();
+ groupData[0]->painters << painter;
+ } else {
+ foreach (const QString &group, painter->groups()) {
+ if (group != QLatin1String("") && !groupIds[group]) {//new group
+ int id = m_nextGroupId++;
+ QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
+ groupIds.insert(group, id);
+ groupData.insert(id, gd);
+ }
+ particleCount += groupData[groupIds[group]]->size();
+ groupData[groupIds[group]]->painters << painter;
+ }
+ }
+ painter->setCount(particleCount);
+ painter->update();//Initial update here
+ return;
+}
+
+void QQuickParticleSystem::emittersChanged()
+{
+ if (!m_componentComplete)
+ return;
+
+ m_emitters.removeAll(0);
+
+
+ QList<int> previousSizes;
+ QList<int> newSizes;
+ for (int i=0; i<m_nextGroupId; i++) {
+ previousSizes << groupData[i]->size();
+ newSizes << 0;
+ }
+
+ foreach (QQuickParticleEmitter* e, m_emitters) {//Populate groups and set sizes.
+ if (!groupIds.contains(e->group())
+ || (!e->group().isEmpty() && !groupIds[e->group()])) {//or it was accidentally inserted by a failed lookup earlier
+ int id = m_nextGroupId++;
+ QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
+ groupIds.insert(e->group(), id);
+ groupData.insert(id, gd);
+ previousSizes << 0;
+ newSizes << 0;
+ }
+ newSizes[groupIds[e->group()]] += e->particleCount();
+ //###: Cull emptied groups?
+ }
+
+ //TODO: Garbage collection?
+ particleCount = 0;
+ for (int i=0; i<m_nextGroupId; i++) {
+ groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
+ particleCount += groupData[i]->size();
+ }
+
+ if (m_debugMode)
+ qDebug() << "Particle system emitters changed. New particle count: " << particleCount;
+
+ if (particleCount > bySysIdx.size())//New datum requests haven't updated it
+ bySysIdx.resize(particleCount);
+
+ foreach (QQuickParticleAffector *a, m_affectors)//Groups may have changed
+ a->m_updateIntSet = true;
+
+ foreach (QQuickParticlePainter *p, m_painters)
+ loadPainter(p);
+
+ if (!m_groups.isEmpty())
+ createEngine();
+
+}
+
+void QQuickParticleSystem::createEngine()
+{
+ if (!m_componentComplete)
+ return;
+ if (stateEngine && m_debugMode)
+ qDebug() << "Resetting Existing Sprite Engine...";
+ //### Solve the losses if size/states go down
+ foreach (QQuickParticleGroup* group, m_groups) {
+ bool exists = false;
+ foreach (const QString &name, groupIds.keys())
+ if (group->name() == name)
+ exists = true;
+ if (!exists) {
+ int id = m_nextGroupId++;
+ QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
+ groupIds.insert(group->name(), id);
+ groupData.insert(id, gd);
+ }
+ }
+
+ if (m_groups.count()) {
+ //Reorder groups List so as to have the same order as groupData
+ QList<QQuickParticleGroup*> newList;
+ for (int i=0; i<m_nextGroupId; i++) {
+ bool exists = false;
+ QString name = groupData[i]->name();
+ foreach (QQuickParticleGroup* existing, m_groups) {
+ if (existing->name() == name) {
+ newList << existing;
+ exists = true;
+ }
+ }
+ if (!exists) {
+ newList << new QQuickParticleGroup(this);
+ newList.back()->setName(name);
+ }
+ }
+ m_groups = newList;
+ QList<QQuickStochasticState*> states;
+ foreach (QQuickParticleGroup* g, m_groups)
+ states << (QQuickStochasticState*)g;
+
+ if (!stateEngine)
+ stateEngine = new QQuickStochasticEngine(this);
+ stateEngine->setCount(particleCount);
+ stateEngine->m_states = states;
+
+ connect(stateEngine, SIGNAL(stateChanged(int)),
+ this, SLOT(particleStateChange(int)));
+
+ } else {
+ if (stateEngine)
+ delete stateEngine;
+ stateEngine = 0;
+ }
+
+}
+
+void QQuickParticleSystem::particleStateChange(int idx)
+{
+ moveGroups(bySysIdx[idx], stateEngine->curState(idx));
+}
+
+void QQuickParticleSystem::moveGroups(QQuickParticleData *d, int newGIdx)
+{
+ if (!d || newGIdx == d->group)
+ return;
+
+ QQuickParticleData* pd = newDatum(newGIdx, false, d->systemIndex);
+ if (!pd)
+ return;
+
+ pd->clone(*d);
+ finishNewDatum(pd);
+
+ d->systemIndex = -1;
+ groupData[d->group]->kill(d);
+}
+
+int QQuickParticleSystem::nextSystemIndex()
+{
+ if (!m_reusableIndexes.isEmpty()) {
+ int ret = *(m_reusableIndexes.begin());
+ m_reusableIndexes.remove(ret);
+ return ret;
+ }
+ if (m_nextIndex >= bySysIdx.size()) {
+ bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
+ if (stateEngine)
+ stateEngine->setCount(bySysIdx.size());
+
+ }
+ return m_nextIndex++;
+}
+
+QQuickParticleData* QQuickParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex)
+{
+ Q_ASSERT(groupId < groupData.count());//XXX shouldn't really be an assert
+
+ QQuickParticleData* ret = groupData[groupId]->newDatum(respectLimits);
+ if (!ret) {
+ return 0;
+ }
+ if (sysIndex == -1) {
+ if (ret->systemIndex == -1)
+ ret->systemIndex = nextSystemIndex();
+ } else {
+ if (ret->systemIndex != -1) {
+ if (stateEngine)
+ stateEngine->stop(ret->systemIndex);
+ m_reusableIndexes << ret->systemIndex;
+ bySysIdx[ret->systemIndex] = 0;
+ }
+ ret->systemIndex = sysIndex;
+ }
+ bySysIdx[ret->systemIndex] = ret;
+
+ if (stateEngine)
+ stateEngine->start(ret->systemIndex, ret->group);
+
+ m_empty = false;
+ return ret;
+}
+
+void QQuickParticleSystem::emitParticle(QQuickParticleData* pd)
+{// called from prepareNextFrame()->emitWindow - enforce?
+ //Account for relative emitter position
+ QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0));
+ if (!offset.isNull()) {
+ pd->x += offset.x();
+ pd->y += offset.y();
+ }
+
+ finishNewDatum(pd);
+}
+
+void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
+{
+ Q_ASSERT(pd);
+ groupData[pd->group]->prepareRecycler(pd);
+
+ foreach (QQuickParticleAffector *a, m_affectors)
+ if (a && a->m_needsReset)
+ a->reset(pd);
+ foreach (QQuickParticlePainter* p, groupData[pd->group]->painters)
+ if (p)
+ p->load(pd);
+}
+
+void QQuickParticleSystem::updateCurrentTime( int currentTime )
+{
+ if (!initialized)
+ return;//error in initialization
+
+ //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time.
+ qreal dt = timeInt / 1000.;
+ timeInt = currentTime;
+ qreal time = timeInt / 1000.;
+ dt = time - dt;
+ needsReset.clear();
+
+ m_emitters.removeAll(0);
+ m_painters.removeAll(0);
+ m_affectors.removeAll(0);
+
+ bool oldClear = m_empty;
+ m_empty = true;
+ foreach (QQuickParticleGroupData* gd, groupData)//Recycle all groups and see if they're out of live particles
+ m_empty = gd->recycle() && m_empty;
+
+ if (stateEngine)
+ stateEngine->updateSprites(timeInt);
+
+ foreach (QQuickParticleEmitter* emitter, m_emitters)
+ emitter->emitWindow(timeInt);
+ foreach (QQuickParticleAffector* a, m_affectors)
+ a->affectSystem(dt);
+ foreach (QQuickParticleData* d, needsReset)
+ foreach (QQuickParticlePainter* p, groupData[d->group]->painters)
+ p->reload(d);
+
+ if (oldClear != m_empty)
+ emptyChanged(m_empty);
+}
+
+int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
+{
+ if (!m_running)
+ return 0;
+ if (!initialized)
+ return 0;//error in initialization
+ p->performPendingCommits();
+ return timeInt;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h
new file mode 100644
index 0000000000..f70cc5af94
--- /dev/null
+++ b/src/particles/qquickparticlesystem_p.h
@@ -0,0 +1,379 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PARTICLESYSTEM_H
+#define PARTICLESYSTEM_H
+
+#include <QtQuick/QQuickItem>
+#include <QElapsedTimer>
+#include <QVector>
+#include <QHash>
+#include <QPointer>
+#include <QSignalMapper>
+#include <private/qquicksprite_p.h>
+#include <QAbstractAnimation>
+#include <QtQml/qqml.h>
+#include <private/qv8engine_p.h> //For QQmlV8Handle
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleSystem;
+class QQuickParticleAffector;
+class QQuickParticleEmitter;
+class QQuickParticlePainter;
+class QQuickParticleData;
+class QQuickParticleSystemAnimation;
+class QQuickStochasticEngine;
+class QQuickSprite;
+class QQuickV8ParticleData;
+class QQuickParticleGroup;
+class QQuickImageParticle;
+
+struct QQuickParticleDataHeapNode{
+ int time;//in ms
+ QSet<QQuickParticleData*> data;//Set ptrs instead?
+};
+
+class QQuickParticleDataHeap {
+ //Idea is to do a binary heap, but which also stores a set of int,Node* so that if the int already exists, you can
+ //add it to the data* list. Pops return the whole list at once.
+public:
+ QQuickParticleDataHeap();
+ void insert(QQuickParticleData* data);
+ void insertTimed(QQuickParticleData* data, int time);
+
+ int top();
+
+ QSet<QQuickParticleData*> pop();
+
+ void clear();
+
+ bool contains(QQuickParticleData*);//O(n), for debugging purposes only
+private:
+ void grow();
+ void swap(int, int);
+ void bubbleUp(int);
+ void bubbleDown(int);
+ int m_size;
+ int m_end;
+ QQuickParticleDataHeapNode m_tmp;
+ QVector<QQuickParticleDataHeapNode> m_data;
+ QHash<int,int> m_lookups;
+};
+
+class Q_AUTOTEST_EXPORT QQuickParticleGroupData {
+public:
+ QQuickParticleGroupData(int id, QQuickParticleSystem* sys);
+ ~QQuickParticleGroupData();
+
+ int size();
+ QString name();
+
+ void setSize(int newSize);
+
+ int index;
+ QSet<QQuickParticlePainter*> painters;//TODO: What if they are dynamically removed?
+
+ //TODO: Refactor particle data list out into a separate class
+ QVector<QQuickParticleData*> data;
+ QQuickParticleDataHeap dataHeap;
+ QSet<int> reusableIndexes;
+ bool recycle(); //Force recycling round, returns true if all indexes are now reusable
+
+ void initList();
+ void kill(QQuickParticleData* d);
+
+ //After calling this, initialize, then call prepareRecycler(d)
+ QQuickParticleData* newDatum(bool respectsLimits);
+
+ //TODO: Find and clean up those that don't get added to the recycler (currently they get lost)
+ void prepareRecycler(QQuickParticleData* d);
+
+private:
+ int m_size;
+ QQuickParticleSystem* m_system;
+};
+
+struct Color4ub {
+ uchar r;
+ uchar g;
+ uchar b;
+ uchar a;
+};
+
+class Q_AUTOTEST_EXPORT QQuickParticleData {
+public:
+ //TODO: QObject like memory management (without the cost, just attached to system)
+ QQuickParticleData(QQuickParticleSystem* sys);
+ ~QQuickParticleData();
+
+ //Convenience functions for working backwards, because parameters are from the start of particle life
+ //If setting multiple parameters at once, doing the conversion yourself will be faster.
+
+ //sets the x accleration without affecting the instantaneous x velocity or position
+ void setInstantaneousAX(qreal ax);
+ //sets the x velocity without affecting the instantaneous x postion
+ void setInstantaneousVX(qreal vx);
+ //sets the instantaneous x postion
+ void setInstantaneousX(qreal x);
+ //sets the y accleration without affecting the instantaneous y velocity or position
+ void setInstantaneousAY(qreal ay);
+ //sets the y velocity without affecting the instantaneous y postion
+ void setInstantaneousVY(qreal vy);
+ //sets the instantaneous Y postion
+ void setInstantaneousY(qreal y);
+
+ //TODO: Slight caching?
+ qreal curX() const;
+ qreal curVX() const;
+ qreal curAX() const { return ax; }
+ qreal curY() const;
+ qreal curVY() const;
+ qreal curAY() const { return ay; }
+
+ int group;
+ QQuickParticleEmitter* e;//### Needed?
+ QQuickParticleSystem* system;
+ int index;
+ int systemIndex;
+
+ //General Position Stuff
+ float x;
+ float y;
+ float t;
+ float lifeSpan;
+ float size;
+ float endSize;
+ float vx;
+ float vy;
+ float ax;
+ float ay;
+
+ //Other stuff, now universally shared
+ Color4ub color;
+ float xx;
+ float xy;
+ float yx;
+ float yy;
+ float rotation;
+ float rotationSpeed;
+ float autoRotate;//Assume that GPUs prefer floats to bools
+ float animIdx;
+ float frameDuration;
+ float frameAt;//Used for duration -1
+ float frameCount;
+ float animT;
+ float animX;
+ float animY;
+ float animWidth;
+ float animHeight;
+ float r;
+ QQuickItem* delegate;
+ int modelIndex;
+ float update;//Used by custom affectors
+
+ //Used by image particle
+ QQuickImageParticle* colorOwner;
+ QQuickImageParticle* rotationOwner;
+ QQuickImageParticle* deformationOwner;
+ QQuickImageParticle* animationOwner;
+
+ void debugDump();
+ bool stillAlive();//Only checks end, because usually that's all you need and it's a little faster.
+ bool alive();
+ float lifeLeft();
+ float curSize();
+ void clone(const QQuickParticleData& other);//Not =, leaves meta-data like index
+ QQmlV8Handle v8Value();
+ void extendLife(float time);
+private:
+ QQuickV8ParticleData* v8Datum;
+};
+
+class Q_AUTOTEST_EXPORT QQuickParticleSystem : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
+ Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged)
+ Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
+
+public:
+ explicit QQuickParticleSystem(QQuickItem *parent = 0);
+ ~QQuickParticleSystem();
+
+ bool isRunning() const
+ {
+ return m_running;
+ }
+
+ int count(){ return particleCount; }
+
+ static const int maxLife = 600000;
+
+signals:
+
+ void systemInitialized();
+ void runningChanged(bool arg);
+ void pausedChanged(bool arg);
+ void emptyChanged(bool arg);
+
+public slots:
+ void start(){setRunning(true);}
+ void stop(){setRunning(false);}
+ void restart(){setRunning(false);setRunning(true);}
+ void pause(){setPaused(true);}
+ void resume(){setPaused(false);}
+
+ void reset();
+ void setRunning(bool arg);
+ void setPaused(bool arg);
+
+ virtual int duration() const { return -1; }
+
+
+protected:
+ //This one only once per frame (effectively)
+ void componentComplete();
+
+private slots:
+ void emittersChanged();
+ void loadPainter(QObject* p);
+ void createEngine(); //Not invoked by sprite engine, unlike Sprite uses
+ void particleStateChange(int idx);
+
+public:
+ //These can be called multiple times per frame, performance critical
+ void emitParticle(QQuickParticleData* p);
+ QQuickParticleData* newDatum(int groupId, bool respectLimits = true, int sysIdx = -1);
+ void finishNewDatum(QQuickParticleData*);
+ void moveGroups(QQuickParticleData *d, int newGIdx);
+ int nextSystemIndex();
+
+ //This one only once per painter per frame
+ int systemSync(QQuickParticlePainter* p);
+
+ //Data members here for ease of related class and auto-test usage. Not "public" API. TODO: d_ptrize
+ QSet<QQuickParticleData*> needsReset;
+ QVector<QQuickParticleData*> bySysIdx; //Another reference to the data (data owned by group), but by sysIdx
+ QHash<QString, int> groupIds;
+ QHash<int, QQuickParticleGroupData*> groupData;
+ QQuickStochasticEngine* stateEngine;
+
+ //Also only here for auto-test usage
+ void updateCurrentTime( int currentTime );
+ QQuickParticleSystemAnimation* m_animation;
+ bool m_running;
+ bool m_debugMode;
+
+ int timeInt;
+ bool initialized;
+ int particleCount;
+
+ void registerParticlePainter(QQuickParticlePainter* p);
+ void registerParticleEmitter(QQuickParticleEmitter* e);
+ void registerParticleAffector(QQuickParticleAffector* a);
+ void registerParticleGroup(QQuickParticleGroup* g);
+
+ static void statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value);
+ static void stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value);
+ bool isPaused() const
+ {
+ return m_paused;
+ }
+
+ bool isEmpty() const
+ {
+ return m_empty;
+ }
+
+private:
+ void initializeSystem();
+ void initGroups();
+ QList<QPointer<QQuickParticleEmitter> > m_emitters;
+ QList<QPointer<QQuickParticleAffector> > m_affectors;
+ QList<QPointer<QQuickParticlePainter> > m_painters;
+ QList<QPointer<QQuickParticlePainter> > m_syncList;
+ QList<QQuickParticleGroup*> m_groups;
+ int m_nextGroupId;
+ int m_nextIndex;
+ QSet<int> m_reusableIndexes;
+ bool m_componentComplete;
+
+ QSignalMapper m_painterMapper;
+ QSignalMapper m_emitterMapper;
+ bool m_paused;
+ bool m_allDead;
+ bool m_empty;
+};
+
+// Internally, this animation drives all the timing. Painters sync up in their updatePaintNode
+class QQuickParticleSystemAnimation : public QAbstractAnimation
+{
+ Q_OBJECT
+public:
+ QQuickParticleSystemAnimation(QQuickParticleSystem* system)
+ : QAbstractAnimation(static_cast<QObject*>(system)), m_system(system)
+ { }
+protected:
+ virtual void updateCurrentTime( int t )
+ {
+ m_system->updateCurrentTime(t);
+ }
+
+ virtual int duration() const
+ {
+ return -1;
+ }
+
+private:
+ QQuickParticleSystem* m_system;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // PARTICLESYSTEM_H
+
+
diff --git a/src/particles/qquickpointattractor.cpp b/src/particles/qquickpointattractor.cpp
new file mode 100644
index 0000000000..a54ee41c1e
--- /dev/null
+++ b/src/particles/qquickpointattractor.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpointattractor_p.h"
+#include <cmath>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Attractor QQuickAttractorAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Attractor allows you to attract particles towards a specific point.
+
+ Note that the size and position of this element affects which particles it affects.
+ The size of the point attracted to is always 0x0, and the location of that point
+ is specified by the pointX and pointY properties.
+
+ Note that Attractor has the standard Item x,y,width and height properties.
+ Like other affectors, these represent the affected area. They
+ do not represent the 0x0 point which is the target of the attraction.
+*/
+
+
+/*!
+ \qmlproperty real QtQuick.Particles2::PointAttractor::pointX
+
+ The x coordinate of the attracting point. This is relative
+ to the x coordinate of the Attractor.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointAttractor::pointY
+
+ The y coordinate of the attracting point. This is relative
+ to the y coordinate of the Attractor.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointAttractor::strength
+
+ The pull, in units per second, to be exerted on an item one pixel away.
+
+ Depending on how the attraction is proportionalToDistance this may have to
+ be very high or very low to have a reasonable effect on particles at a
+ distance.
+*/
+/*!
+ \qmlproperty AffectableParameter QtQuick.Particles2::Attractor::affectedParameter
+
+ What attribute of particles is directly affected.
+ \list
+ \li Attractor.Position
+ \li Attractor.Velocity
+ \li Attractor.Acceleration
+ \endlist
+*/
+/*!
+ \qmlproperty Proportion QtQuick.Particles2::Attractor::proportionalToDistance
+
+ How the distance from the particle to the point affects the strength of the attraction.
+
+ \list
+ \li Attractor.Constant
+ \li Attractor.Linear
+ \li Attractor.InverseLinear
+ \li Attractor.Quadratic
+ \li Attractor.InverseQuadratic
+ \endlist
+*/
+
+
+QQuickAttractorAffector::QQuickAttractorAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0)
+ , m_physics(Velocity), m_proportionalToDistance(Linear)
+{
+}
+
+bool QQuickAttractorAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ if (m_strength == 0.0)
+ return false;
+ qreal dx = m_x+m_offset.x() - d->curX();
+ qreal dy = m_y+m_offset.y() - d->curY();
+ qreal r = sqrt((dx*dx) + (dy*dy));
+ qreal theta = atan2(dy,dx);
+ qreal ds = 0;
+ switch (m_proportionalToDistance){
+ case InverseQuadratic:
+ ds = (m_strength / qMax<qreal>(1.,r*r));
+ break;
+ case InverseLinear:
+ ds = (m_strength / qMax<qreal>(1.,r));
+ break;
+ case Quadratic:
+ ds = (m_strength * qMax<qreal>(1.,r*r));
+ break;
+ case Linear:
+ ds = (m_strength * qMax<qreal>(1.,r));
+ break;
+ default: //also Constant
+ ds = m_strength;
+ }
+ ds *= dt;
+ dx = ds * cos(theta);
+ dy = ds * sin(theta);
+ qreal vx,vy;
+ switch (m_physics){
+ case Position:
+ d->x = (d->x + dx);
+ d->y = (d->y + dy);
+ break;
+ case Acceleration:
+ d->setInstantaneousAX(d->ax + dx);
+ d->setInstantaneousAY(d->ay + dy);
+ break;
+ case Velocity: //also default
+ default:
+ vx = d->curVX();
+ vy = d->curVY();
+ d->setInstantaneousVX(vx + dx);
+ d->setInstantaneousVY(vy + dy);
+ }
+
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/particles/qquickpointattractor_p.h b/src/particles/qquickpointattractor_p.h
new file mode 100644
index 0000000000..85b7a9aa30
--- /dev/null
+++ b/src/particles/qquickpointattractor_p.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ATTRACTORAFFECTOR_H
+#define ATTRACTORAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAttractorAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged)
+ Q_PROPERTY(qreal pointX READ pointX WRITE setPointX NOTIFY pointXChanged)
+ Q_PROPERTY(qreal pointY READ pointY WRITE setPointY NOTIFY pointYChanged)
+ Q_PROPERTY(AffectableParameters affectedParameter READ affectedParameter WRITE setAffectedParameter NOTIFY affectedParameterChanged)
+ Q_PROPERTY(Proportion proportionalToDistance READ proportionalToDistance WRITE setProportionalToDistance NOTIFY proportionalToDistanceChanged)
+ Q_ENUMS(AffectableParameters)
+ Q_ENUMS(Proportion)
+
+public:
+ enum Proportion{
+ Constant,
+ Linear,
+ Quadratic,
+ InverseLinear,
+ InverseQuadratic
+ };
+
+ enum AffectableParameters {
+ Position,
+ Velocity,
+ Acceleration
+ };
+
+ explicit QQuickAttractorAffector(QQuickItem *parent = 0);
+
+ qreal strength() const
+ {
+ return m_strength;
+ }
+
+ qreal pointX() const
+ {
+ return m_x;
+ }
+
+ qreal pointY() const
+ {
+ return m_y;
+ }
+
+ AffectableParameters affectedParameter() const
+ {
+ return m_physics;
+ }
+
+ Proportion proportionalToDistance() const
+ {
+ return m_proportionalToDistance;
+ }
+
+signals:
+
+ void strengthChanged(qreal arg);
+
+ void pointXChanged(qreal arg);
+
+ void pointYChanged(qreal arg);
+
+ void affectedParameterChanged(AffectableParameters arg);
+
+ void proportionalToDistanceChanged(Proportion arg);
+
+public slots:
+void setStrength(qreal arg)
+{
+ if (m_strength != arg) {
+ m_strength = arg;
+ emit strengthChanged(arg);
+ }
+}
+
+void setPointX(qreal arg)
+{
+ if (m_x != arg) {
+ m_x = arg;
+ emit pointXChanged(arg);
+ }
+}
+
+void setPointY(qreal arg)
+{
+ if (m_y != arg) {
+ m_y = arg;
+ emit pointYChanged(arg);
+ }
+}
+void setAffectedParameter(AffectableParameters arg)
+{
+ if (m_physics != arg) {
+ m_physics = arg;
+ emit affectedParameterChanged(arg);
+ }
+}
+
+void setProportionalToDistance(Proportion arg)
+{
+ if (m_proportionalToDistance != arg) {
+ m_proportionalToDistance = arg;
+ emit proportionalToDistanceChanged(arg);
+ }
+}
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+private:
+qreal m_strength;
+qreal m_x;
+qreal m_y;
+AffectableParameters m_physics;
+Proportion m_proportionalToDistance;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // ATTRACTORAFFECTOR_H
diff --git a/src/particles/qquickpointdirection.cpp b/src/particles/qquickpointdirection.cpp
new file mode 100644
index 0000000000..e35eb4854c
--- /dev/null
+++ b/src/particles/qquickpointdirection.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpointdirection_p.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass PointDirection QQuickPointDirection
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Direction
+ \brief The PointDirection element allows you to specify a direction that varies in x and y components
+
+ The PointDirection element allows both the specification of a direction by x and y components,
+ as well as varying the parameters by x or y component.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointDirection::x
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointDirection::y
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointDirection::xVariation
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::PointDirection::yVariation
+*/
+
+QQuickPointDirection::QQuickPointDirection(QObject *parent) :
+ QQuickDirection(parent)
+ , m_x(0)
+ , m_y(0)
+ , m_xVariation(0)
+ , m_yVariation(0)
+{
+}
+
+const QPointF QQuickPointDirection::sample(const QPointF &)
+{
+ QPointF ret;
+ ret.setX(m_x - m_xVariation + rand() / float(RAND_MAX) * m_xVariation * 2);
+ ret.setY(m_y - m_yVariation + rand() / float(RAND_MAX) * m_yVariation * 2);
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickpointdirection_p.h b/src/particles/qquickpointdirection_p.h
new file mode 100644
index 0000000000..7b18e6d8d7
--- /dev/null
+++ b/src/particles/qquickpointdirection_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef POINTVECTOR_H
+#define POINTVECTOR_H
+#include "qquickdirection_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPointDirection : public QQuickDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
+ Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged)
+ Q_PROPERTY(qreal xVariation READ xVariation WRITE setXVariation NOTIFY xVariationChanged)
+ Q_PROPERTY(qreal yVariation READ yVariation WRITE setYVariation NOTIFY yVariationChanged)
+public:
+ explicit QQuickPointDirection(QObject *parent = 0);
+ virtual const QPointF sample(const QPointF &from);
+ qreal x() const
+ {
+ return m_x;
+ }
+
+ qreal y() const
+ {
+ return m_y;
+ }
+
+ qreal xVariation() const
+ {
+ return m_xVariation;
+ }
+
+ qreal yVariation() const
+ {
+ return m_yVariation;
+ }
+
+signals:
+
+ void xChanged(qreal arg);
+
+ void yChanged(qreal arg);
+
+ void xVariationChanged(qreal arg);
+
+ void yVariationChanged(qreal arg);
+
+public slots:
+ void setX(qreal arg)
+ {
+ if (m_x != arg) {
+ m_x = arg;
+ emit xChanged(arg);
+ }
+ }
+
+ void setY(qreal arg)
+ {
+ if (m_y != arg) {
+ m_y = arg;
+ emit yChanged(arg);
+ }
+ }
+
+ void setXVariation(qreal arg)
+ {
+ if (m_xVariation != arg) {
+ m_xVariation = arg;
+ emit xVariationChanged(arg);
+ }
+ }
+
+ void setYVariation(qreal arg)
+ {
+ if (m_yVariation != arg) {
+ m_yVariation = arg;
+ emit yVariationChanged(arg);
+ }
+ }
+
+private:
+
+ qreal m_x;
+ qreal m_y;
+ qreal m_xVariation;
+ qreal m_yVariation;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // POINTVECTOR_H
diff --git a/src/particles/qquickrectangleextruder.cpp b/src/particles/qquickrectangleextruder.cpp
new file mode 100644
index 0000000000..ad2207ca9a
--- /dev/null
+++ b/src/particles/qquickrectangleextruder.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickrectangleextruder_p.h"
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass RectangleShape QQuickRectangleExtruder
+ \inqmlmodule QtQuick.Particles 2
+ \brief The RectangleShape element allows you to specify an area for affectors and emitter.
+
+ Just a rectangle.
+*/
+
+QQuickRectangleExtruder::QQuickRectangleExtruder(QObject *parent) :
+ QQuickParticleExtruder(parent), m_fill(true)
+{
+}
+
+QPointF QQuickRectangleExtruder::extrude(const QRectF &rect)
+{
+ if (m_fill)
+ return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(),
+ ((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
+ int side = rand() % 4;
+ switch (side){//TODO: Doesn't this overlap the corners?
+ case 0:
+ return QPointF(rect.x(),
+ ((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
+ case 1:
+ return QPointF(rect.width() + rect.x(),
+ ((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
+ case 2:
+ return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(),
+ rect.y());
+ default:
+ return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(),
+ rect.height() + rect.y());
+ }
+}
+
+bool QQuickRectangleExtruder::contains(const QRectF &bounds, const QPointF &point)
+{
+ return bounds.contains(point);
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickrectangleextruder_p.h b/src/particles/qquickrectangleextruder_p.h
new file mode 100644
index 0000000000..1d4f8cc439
--- /dev/null
+++ b/src/particles/qquickrectangleextruder_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef RECTANGLEEXTRUDER_H
+#define RECTANGLEEXTRUDER_H
+
+#include "qquickparticleextruder_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickRectangleExtruder : public QQuickParticleExtruder
+{
+ Q_OBJECT
+ Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)
+
+public:
+ explicit QQuickRectangleExtruder(QObject *parent = 0);
+ virtual QPointF extrude(const QRectF &);
+ virtual bool contains(const QRectF &bounds, const QPointF &point);
+ bool fill() const
+ {
+ return m_fill;
+ }
+
+signals:
+
+ void fillChanged(bool arg);
+
+public slots:
+
+ void setFill(bool arg)
+ {
+ if (m_fill != arg) {
+ m_fill = arg;
+ emit fillChanged(arg);
+ }
+ }
+protected:
+ bool m_fill;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // RectangleEXTRUDER_H
diff --git a/src/particles/qquickspritegoal.cpp b/src/particles/qquickspritegoal.cpp
new file mode 100644
index 0000000000..95d913cafa
--- /dev/null
+++ b/src/particles/qquickspritegoal.cpp
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickspritegoal_p.h"
+#include <private/qquickspriteengine_p.h>
+#include <private/qquicksprite_p.h>
+#include "qquickimageparticle_p.h"
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass SpriteGoal QQuickSpriteGoalAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The SpriteGoal Affector allows you to change the state of a sprite particle.
+
+*/
+/*!
+ \qmlproperty string QtQuick.Particles2::SpriteGoal::goalState
+
+ The name of the Sprite which the affected particles should move to.
+
+ Sprite states have defined durations and transitions between them, setting goalState
+ will cause it to disregard any path weightings (including 0) and head down the path
+ which will reach the goalState quickest. It will pass through intermediate states
+ on that path.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::SpriteGoal::jump
+
+ If true, affected sprites will jump directly to the goal state instead of taking the
+ the shortest valid path to get there. They will also not finish their current state,
+ but immediately move to the beginning of the goal state.
+
+ Default is false.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::SpriteGoal::systemStates
+
+ deprecated, use GroupGoal instead
+*/
+
+QQuickSpriteGoalAffector::QQuickSpriteGoalAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent),
+ m_goalIdx(-1),
+ m_lastEngine(0),
+ m_jump(false),
+ m_systemStates(false),
+ m_notUsingEngine(false)
+{
+ m_ignoresTime = true;
+}
+
+void QQuickSpriteGoalAffector::updateStateIndex(QQuickStochasticEngine* e)
+{
+ if (m_systemStates){
+ m_goalIdx = m_system->groupIds[m_goalState];
+ }else{
+ m_lastEngine = e;
+ for (int i=0; i<e->stateCount(); i++){
+ if (e->state(i)->name() == m_goalState){
+ m_goalIdx = i;
+ return;
+ }
+ }
+ m_goalIdx = -1;//Can't find it
+ }
+}
+
+void QQuickSpriteGoalAffector::setGoalState(QString arg)
+{
+ if (m_goalState != arg) {
+ m_goalState = arg;
+ emit goalStateChanged(arg);
+ if (m_goalState.isEmpty())
+ m_goalIdx = -1;
+ else
+ m_goalIdx = -2;
+ }
+}
+
+bool QQuickSpriteGoalAffector::affectParticle(QQuickParticleData *d, qreal dt)
+{
+ Q_UNUSED(dt);
+ QQuickStochasticEngine *engine = 0;
+ if (!m_systemStates){
+ //TODO: Affect all engines
+ foreach (QQuickParticlePainter *p, m_system->groupData[d->group]->painters)
+ if (qobject_cast<QQuickImageParticle*>(p))
+ engine = qobject_cast<QQuickImageParticle*>(p)->spriteEngine();
+ }else{
+ engine = m_system->stateEngine;
+ if (!engine)
+ m_notUsingEngine = true;
+ }
+ if (!engine && !m_notUsingEngine)
+ return false;
+
+ if (m_goalIdx == -2 || engine != m_lastEngine)
+ updateStateIndex(engine);
+ int index = d->index;
+ if (m_systemStates)
+ index = d->systemIndex;
+ if (m_notUsingEngine){//systemStates && no stochastic states defined. So cut out the engine
+ //TODO: It's possible to move to a group that is intermediate and not used by painters or emitters - but right now that will redirect to the default group
+ m_system->moveGroups(d, m_goalIdx);
+ }else if (engine->curState(index) != m_goalIdx){
+ engine->setGoal(m_goalIdx, index, m_jump);
+ return true; //Doesn't affect particle data, but necessary for onceOff
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickspritegoal_p.h b/src/particles/qquickspritegoal_p.h
new file mode 100644
index 0000000000..2b6b4f28fc
--- /dev/null
+++ b/src/particles/qquickspritegoal_p.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SPRITEGOALAFFECTOR_H
+#define SPRITEGOALAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+#include <QtQml/qqmlinfo.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickStochasticEngine;
+
+class QQuickSpriteGoalAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged)
+ Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged)
+ Q_PROPERTY(bool systemStates READ systemStates WRITE setSystemStates NOTIFY systemStatesChanged)
+public:
+ explicit QQuickSpriteGoalAffector(QQuickItem *parent = 0);
+
+ QString goalState() const
+ {
+ return m_goalState;
+ }
+
+ bool jump() const
+ {
+ return m_jump;
+ }
+ bool systemStates() const
+ {
+ return m_systemStates;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+signals:
+
+ void goalStateChanged(QString arg);
+
+ void jumpChanged(bool arg);
+
+ void systemStatesChanged(bool arg);
+
+public slots:
+
+void setGoalState(QString arg);
+
+void setJump(bool arg)
+{
+ if (m_jump != arg) {
+ m_jump = arg;
+ emit jumpChanged(arg);
+ }
+}
+
+void setSystemStates(bool arg)
+{
+ if (m_systemStates != arg) {
+ //TODO: GroupGoal was added (and this deprecated) Oct 4 - remove it in a few weeks.
+ qmlInfo(this) << "systemStates is deprecated and will be removed soon. Use GroupGoal instead.";
+ m_systemStates = arg;
+ emit systemStatesChanged(arg);
+ }
+}
+
+private:
+ void updateStateIndex(QQuickStochasticEngine* e);
+ QString m_goalState;
+ int m_goalIdx;
+ QQuickStochasticEngine* m_lastEngine;
+ bool m_jump;
+ bool m_systemStates;
+
+ bool m_notUsingEngine;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // SPRITEGOALAFFECTOR_H
diff --git a/src/particles/qquicktargetdirection.cpp b/src/particles/qquicktargetdirection.cpp
new file mode 100644
index 0000000000..695684dfde
--- /dev/null
+++ b/src/particles/qquicktargetdirection.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktargetdirection_p.h"
+#include "qquickparticleemitter_p.h"
+#include <cmath>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass TargetDirection QQuickTargetDirection
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Direction
+ \brief The TargetDirection element allows you to specify a direction towards the target point
+
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::targetX
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::targetY
+*/
+/*!
+ \qmlproperty Item QtQuick.Particles2::TargetDirection::targetItem
+ If specified, this will take precedence over targetX and targetY.
+ The targeted point will be the center of the specified Item
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::targetVariation
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::magnitude
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TargetDirection::magnitudeVariation
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::TargetDirection::proportionalMagnitude
+
+ If true, then the value of magnitude and magnitudeVariation shall be interpreted as multiples
+ of the distance between the source point and the target point, per second.
+
+ If false(default), then the value of magnitude and magnitudeVariation shall be interpreted as
+ pixels per second.
+*/
+
+QQuickTargetDirection::QQuickTargetDirection(QObject *parent) :
+ QQuickDirection(parent)
+ , m_targetX(0)
+ , m_targetY(0)
+ , m_targetVariation(0)
+ , m_proportionalMagnitude(false)
+ , m_magnitude(0)
+ , m_magnitudeVariation(0)
+ , m_targetItem(0)
+{
+}
+
+const QPointF QQuickTargetDirection::sample(const QPointF &from)
+{
+ //###This approach loses interpolating the last position of the target (like we could with the emitter) is it worthwhile?
+ QPointF ret;
+ qreal targetX;
+ qreal targetY;
+ if (m_targetItem){
+ QQuickParticleEmitter* parentEmitter = qobject_cast<QQuickParticleEmitter*>(parent());
+ targetX = m_targetItem->width()/2;
+ targetY = m_targetItem->height()/2;
+ if (!parentEmitter){
+ qWarning() << "Directed vector is not a child of the emitter. Mapping of target item coordinates may fail.";
+ targetX += m_targetItem->x();
+ targetY += m_targetItem->y();
+ }else{
+ ret = parentEmitter->mapFromItem(m_targetItem, QPointF(targetX, targetY));
+ targetX = ret.x();
+ targetY = ret.y();
+ }
+ }else{
+ targetX = m_targetX;
+ targetY = m_targetY;
+ }
+ targetX += 0 - from.x() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2;
+ targetY += 0 - from.y() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2;
+ qreal theta = atan2(targetY, targetX);
+ qreal mag = m_magnitude + rand()/(float)RAND_MAX * m_magnitudeVariation * 2 - m_magnitudeVariation;
+ if (m_proportionalMagnitude)
+ mag *= sqrt(targetX * targetX + targetY * targetY);
+ ret.setX(mag * cos(theta));
+ ret.setY(mag * sin(theta));
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquicktargetdirection_p.h b/src/particles/qquicktargetdirection_p.h
new file mode 100644
index 0000000000..0e0e942ca8
--- /dev/null
+++ b/src/particles/qquicktargetdirection_p.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DIRECTEDVECTOR_H
+#define DIRECTEDVECTOR_H
+#include "qquickdirection_p.h"
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QQuickTargetDirection : public QQuickDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal targetX READ targetX WRITE setTargetX NOTIFY targetXChanged)
+ Q_PROPERTY(qreal targetY READ targetY WRITE setTargetY NOTIFY targetYChanged)
+ //If targetItem is set, X/Y are ignored. Aims at middle of item, use variation for variation
+ Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
+
+ Q_PROPERTY(qreal targetVariation READ targetVariation WRITE setTargetVariation NOTIFY targetVariationChanged)
+
+ //TODO: An enum would be better
+ Q_PROPERTY(bool proportionalMagnitude READ proportionalMagnitude WRITE setProportionalMagnitude NOTIFY proprotionalMagnitudeChanged)
+ Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged)
+ Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged)
+
+public:
+ explicit QQuickTargetDirection(QObject *parent = 0);
+ virtual const QPointF sample(const QPointF &from);
+
+ qreal targetX() const
+ {
+ return m_targetX;
+ }
+
+ qreal targetY() const
+ {
+ return m_targetY;
+ }
+
+ qreal targetVariation() const
+ {
+ return m_targetVariation;
+ }
+
+ qreal magnitude() const
+ {
+ return m_magnitude;
+ }
+
+ bool proportionalMagnitude() const
+ {
+ return m_proportionalMagnitude;
+ }
+
+ qreal magnitudeVariation() const
+ {
+ return m_magnitudeVariation;
+ }
+
+ QQuickItem* targetItem() const
+ {
+ return m_targetItem;
+ }
+
+signals:
+
+ void targetXChanged(qreal arg);
+
+ void targetYChanged(qreal arg);
+
+ void targetVariationChanged(qreal arg);
+
+ void magnitudeChanged(qreal arg);
+
+ void proprotionalMagnitudeChanged(bool arg);
+
+ void magnitudeVariationChanged(qreal arg);
+
+ void targetItemChanged(QQuickItem* arg);
+
+public slots:
+ void setTargetX(qreal arg)
+ {
+ if (m_targetX != arg) {
+ m_targetX = arg;
+ emit targetXChanged(arg);
+ }
+ }
+
+ void setTargetY(qreal arg)
+ {
+ if (m_targetY != arg) {
+ m_targetY = arg;
+ emit targetYChanged(arg);
+ }
+ }
+
+ void setTargetVariation(qreal arg)
+ {
+ if (m_targetVariation != arg) {
+ m_targetVariation = arg;
+ emit targetVariationChanged(arg);
+ }
+ }
+
+ void setMagnitude(qreal arg)
+ {
+ if (m_magnitude != arg) {
+ m_magnitude = arg;
+ emit magnitudeChanged(arg);
+ }
+ }
+
+ void setProportionalMagnitude(bool arg)
+ {
+ if (m_proportionalMagnitude != arg) {
+ m_proportionalMagnitude = arg;
+ emit proprotionalMagnitudeChanged(arg);
+ }
+ }
+
+ void setMagnitudeVariation(qreal arg)
+ {
+ if (m_magnitudeVariation != arg) {
+ m_magnitudeVariation = arg;
+ emit magnitudeVariationChanged(arg);
+ }
+ }
+
+ void setTargetItem(QQuickItem* arg)
+ {
+ if (m_targetItem != arg) {
+ m_targetItem = arg;
+ emit targetItemChanged(arg);
+ }
+ }
+
+private:
+ qreal m_targetX;
+ qreal m_targetY;
+ qreal m_targetVariation;
+ bool m_proportionalMagnitude;
+ qreal m_magnitude;
+ qreal m_magnitudeVariation;
+ QQuickItem *m_targetItem;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // DIRECTEDVECTOR_H
diff --git a/src/particles/qquicktrailemitter.cpp b/src/particles/qquicktrailemitter.cpp
new file mode 100644
index 0000000000..32f8763599
--- /dev/null
+++ b/src/particles/qquicktrailemitter.cpp
@@ -0,0 +1,285 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktrailemitter_p.h"
+#include <private/qqmlengine_p.h>
+#include <cmath>
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass TrailEmitter QQuickTrailEmitter
+ \inqmlmodule QtQuick.Particles 2
+ \inherits QQuickParticleEmitter
+ \brief The TrailEmitter element allows you to emit logical particles from other logical particles.
+
+ This element emits logical particles into the ParticleSystem, with the
+ starting positions based on those of other logical particles.
+*/
+QQuickTrailEmitter::QQuickTrailEmitter(QQuickItem *parent) :
+ QQuickParticleEmitter(parent)
+ , m_particlesPerParticlePerSecond(0)
+ , m_lastTimeStamp(0)
+ , m_emitterXVariation(0)
+ , m_emitterYVariation(0)
+ , m_followCount(0)
+ , m_emissionExtruder(0)
+ , m_defaultEmissionExtruder(new QQuickParticleExtruder(this))
+{
+ //TODO: If followed increased their size
+ connect(this, SIGNAL(followChanged(QString)),
+ this, SLOT(recalcParticlesPerSecond()));
+ connect(this, SIGNAL(particleDurationChanged(int)),
+ this, SLOT(recalcParticlesPerSecond()));
+ connect(this, SIGNAL(particlesPerParticlePerSecondChanged(int)),
+ this, SLOT(recalcParticlesPerSecond()));
+}
+
+/*!
+ \qmlproperty string QtQuick.Particles2::TrailEmitter::follow
+
+ The type of logical particle which this is emitting from.
+*/
+/*!
+ \qmlproperty qreal QtQuick.Particles2::TrailEmitter::speedFromMovement
+
+ If this value is non-zero, then any movement of the emitter will provide additional
+ starting velocity to the particles based on the movement. The additional vector will be the
+ same angle as the emitter's movement, with a magnitude that is the magnitude of the emitters
+ movement multiplied by speedFromMovement.
+
+ Default value is 0.
+*/
+/*!
+ \qmlproperty Shape QtQuick.Particles2::TrailEmitter::emitShape
+
+ As the area of a TrailEmitter is the area it follows, a separate shape can be provided
+ to be the shape it emits out of. This shape has width and height specified by emitWidth
+ and emitHeight, and is centered on the followed particle's position.
+
+ The default shape is a filled Rectangle.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TrailEmitter::emitWidth
+
+ The width in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize,
+ the width will be the current size of the particle being followed.
+
+ Default is 0.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TrailEmitter::emitHeight
+
+ The height in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize,
+ the height will be the current size of the particle being followed.
+
+ Default is 0.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::TrailEmitter::emitRatePerParticle
+*/
+/*!
+ \qmlsignal QtQuick.Particles2::TrailEmitter::emitFollowParticles(Array particles, real followed)
+
+ This handler is called when particles are emitted from the \a followed particle. \a particles contains an array of particle objects which can be directly manipulated.
+
+ If you use this signal handler, emitParticles will not be emitted.
+
+*/
+
+bool QQuickTrailEmitter::isEmitFollowConnected()
+{
+ static int idx = QObjectPrivate::get(this)->signalIndex("emitFollowParticles(QQmlV8Handle,QQmlV8Handle)");
+ return QObjectPrivate::get(this)->isSignalConnected(idx);
+}
+
+void QQuickTrailEmitter::recalcParticlesPerSecond(){
+ if (!m_system)
+ return;
+ m_followCount = m_system->groupData[m_system->groupIds[m_follow]]->size();
+ if (!m_followCount){
+ setParticlesPerSecond(1);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS)
+ }else{
+ setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount);
+ m_lastEmission.resize(m_followCount);
+ m_lastEmission.fill(m_lastTimeStamp);
+ }
+}
+
+void QQuickTrailEmitter::reset()
+{
+ m_followCount = 0;
+}
+
+void QQuickTrailEmitter::emitWindow(int timeStamp)
+{
+ if (m_system == 0)
+ return;
+ if (!m_enabled && !m_pulseLeft && m_burstQueue.isEmpty())
+ return;
+ if (m_followCount != m_system->groupData[m_system->groupIds[m_follow]]->size()){
+ qreal oldPPS = m_particlesPerSecond;
+ recalcParticlesPerSecond();
+ if (m_particlesPerSecond != oldPPS)
+ return;//system may need to update
+ }
+
+ if (m_pulseLeft){
+ m_pulseLeft -= timeStamp - m_lastTimeStamp * 1000.;
+ if (m_pulseLeft < 0){
+ timeStamp += m_pulseLeft;
+ m_pulseLeft = 0;
+ }
+ }
+
+ //TODO: Implement startTime and speedFromMovement
+ qreal time = timeStamp / 1000.;
+ qreal particleRatio = 1. / m_particlesPerParticlePerSecond;
+ qreal pt;
+ qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0;
+
+ //Have to map it into this system, because particlesystem automaps it back
+ QPointF offset = m_system->mapFromItem(this, QPointF(0, 0));
+ qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize;
+
+ int gId = m_system->groupIds[m_follow];
+ int gId2 = m_system->groupIds[m_group];
+ for (int i=0; i<m_system->groupData[gId]->data.count(); i++) {
+ QQuickParticleData *d = m_system->groupData[gId]->data[i];
+ if (!d->stillAlive()){
+ m_lastEmission[i] = time; //Should only start emitting when it returns to life
+ continue;
+ }
+ pt = m_lastEmission[i];
+ if (pt < d->t)
+ pt = d->t;
+ if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now
+ pt = time - maxLife;
+
+ if ((width() || height()) && !effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){
+ m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside
+ continue;
+ }
+
+ QList<QQuickParticleData*> toEmit;
+
+ while (pt < time || !m_burstQueue.isEmpty()){
+ QQuickParticleData* datum = m_system->newDatum(gId2, !m_overwrite);
+ if (datum){//else, skip this emission
+ datum->e = this;//###useful?
+
+ // Particle timestamp
+ datum->t = pt;
+ datum->lifeSpan =
+ (m_particleDuration
+ + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
+ / 1000.0;
+
+ // Particle position
+ // Note that burst location doesn't get used for follow emitter
+ qreal followT = pt - d->t;
+ qreal followT2 = followT * followT * 0.5;
+ qreal eW = m_emitterXVariation < 0 ? d->curSize() : m_emitterXVariation;
+ qreal eH = m_emitterYVariation < 0 ? d->curSize() : m_emitterYVariation;
+ //Subtract offset, because PS expects this in emitter coordinates
+ QRectF boundsRect(d->x - offset.x() + d->vx * followT + d->ax * followT2 - eW/2,
+ d->y - offset.y() + d->vy * followT + d->ay * followT2 - eH/2,
+ eW, eH);
+
+ QQuickParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder;
+ const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect);
+ datum->x = newPos.x();
+ datum->y = newPos.y();
+
+ // Particle speed
+ const QPointF &speed = m_speed->sample(newPos);
+ datum->vx = speed.x()
+ + m_speed_from_movement * d->vx;
+ datum->vy = speed.y()
+ + m_speed_from_movement * d->vy;
+
+ // Particle acceleration
+ const QPointF &accel = m_acceleration->sample(newPos);
+ datum->ax = accel.x();
+ datum->ay = accel.y();
+
+ // Particle size
+ float sizeVariation = -m_particleSizeVariation
+ + rand() / float(RAND_MAX) * m_particleSizeVariation * 2;
+
+ float size = qMax((qreal)0.0, m_particleSize + sizeVariation);
+ float endSize = qMax((qreal)0.0, sizeAtEnd + sizeVariation);
+
+ datum->size = size * float(m_enabled);
+ datum->endSize = endSize * float(m_enabled);
+
+ toEmit << datum;
+
+ m_system->emitParticle(datum);
+ }
+ if (!m_burstQueue.isEmpty()){
+ m_burstQueue.first().first--;
+ if (m_burstQueue.first().first <= 0)
+ m_burstQueue.pop_front();
+ }else{
+ pt += particleRatio;
+ }
+ }
+
+ if (isEmitConnected() || isEmitFollowConnected()) {
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(QQmlEnginePrivate::getV8Engine(qmlEngine(this))->context());
+ v8::Handle<v8::Array> array = v8::Array::New(toEmit.size());
+ for (int i=0; i<toEmit.size(); i++)
+ array->Set(i, toEmit[i]->v8Value().toHandle());
+
+ if (isEmitFollowConnected())
+ emitFollowParticles(QQmlV8Handle::fromHandle(array), d->v8Value());//A chance for many arbitrary JS changes
+ else if (isEmitConnected())
+ emitParticles(QQmlV8Handle::fromHandle(array));//A chance for arbitrary JS changes
+ }
+ foreach (QQuickParticleData* d, toEmit)
+ m_system->emitParticle(d);
+ m_lastEmission[d->index] = pt;
+ }
+
+ m_lastTimeStamp = time;
+}
+QT_END_NAMESPACE
diff --git a/src/particles/qquicktrailemitter_p.h b/src/particles/qquicktrailemitter_p.h
new file mode 100644
index 0000000000..0b63a444ec
--- /dev/null
+++ b/src/particles/qquicktrailemitter_p.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FOLLOWEMITTER_H
+#define FOLLOWEMITTER_H
+#include "qquickparticleemitter_p.h"
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickTrailEmitter : public QQuickParticleEmitter
+{
+ Q_OBJECT
+ Q_PROPERTY(QString follow READ follow WRITE setFollow NOTIFY followChanged)
+ Q_PROPERTY(int emitRatePerParticle READ particlesPerParticlePerSecond WRITE setParticlesPerParticlePerSecond NOTIFY particlesPerParticlePerSecondChanged)
+
+ Q_PROPERTY(QQuickParticleExtruder* emitShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged)
+ Q_PROPERTY(qreal emitHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged)
+ Q_PROPERTY(qreal emitWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged)
+
+ Q_ENUMS(EmitSize)
+public:
+ enum EmitSize {
+ ParticleSize = -2//Anything less than 0 will do
+ };
+ explicit QQuickTrailEmitter(QQuickItem *parent = 0);
+ virtual void emitWindow(int timeStamp);
+ virtual void reset();
+
+ int particlesPerParticlePerSecond() const
+ {
+ return m_particlesPerParticlePerSecond;
+ }
+
+ qreal emitterXVariation() const
+ {
+ return m_emitterXVariation;
+ }
+
+ qreal emitterYVariation() const
+ {
+ return m_emitterYVariation;
+ }
+
+ QString follow() const
+ {
+ return m_follow;
+ }
+
+ QQuickParticleExtruder* emissonShape() const
+ {
+ return m_emissionExtruder;
+ }
+
+signals:
+ void emitFollowParticles(QQmlV8Handle particles, QQmlV8Handle followed);
+
+ void particlesPerParticlePerSecondChanged(int arg);
+
+ void emitterXVariationChanged(qreal arg);
+
+ void emitterYVariationChanged(qreal arg);
+
+ void followChanged(QString arg);
+
+ void emissionShapeChanged(QQuickParticleExtruder* arg);
+
+public slots:
+
+ void setParticlesPerParticlePerSecond(int arg)
+ {
+ if (m_particlesPerParticlePerSecond != arg) {
+ m_particlesPerParticlePerSecond = arg;
+ emit particlesPerParticlePerSecondChanged(arg);
+ }
+ }
+ void setEmitterXVariation(qreal arg)
+ {
+ if (m_emitterXVariation != arg) {
+ m_emitterXVariation = arg;
+ emit emitterXVariationChanged(arg);
+ }
+ }
+
+ void setEmitterYVariation(qreal arg)
+ {
+ if (m_emitterYVariation != arg) {
+ m_emitterYVariation = arg;
+ emit emitterYVariationChanged(arg);
+ }
+ }
+
+ void setFollow(QString arg)
+ {
+ if (m_follow != arg) {
+ m_follow = arg;
+ emit followChanged(arg);
+ }
+ }
+
+ void setEmissionShape(QQuickParticleExtruder* arg)
+ {
+ if (m_emissionExtruder != arg) {
+ m_emissionExtruder = arg;
+ emit emissionShapeChanged(arg);
+ }
+ }
+
+private slots:
+ void recalcParticlesPerSecond();
+
+private:
+ QSet<QQuickParticleData*> m_pending;
+ QVector<qreal> m_lastEmission;
+ int m_particlesPerParticlePerSecond;
+ qreal m_lastTimeStamp;
+ qreal m_emitterXVariation;
+ qreal m_emitterYVariation;
+ QString m_follow;
+ int m_followCount;
+ QQuickParticleExtruder* m_emissionExtruder;
+ QQuickParticleExtruder* m_defaultEmissionExtruder;
+ bool isEmitFollowConnected();
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // FOLLOWEMITTER_H
diff --git a/src/particles/qquickturbulence.cpp b/src/particles/qquickturbulence.cpp
new file mode 100644
index 0000000000..18ecc6a4cd
--- /dev/null
+++ b/src/particles/qquickturbulence.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickturbulence_p.h"
+#include "qquickparticlepainter_p.h"//TODO: Why was this needed again?
+#include <cmath>
+#include <cstdlib>
+#include <QDebug>
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass Turbulence QQuickTurbulenceAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief Turbulence provides fluid like forces based on a noise image.
+
+ The Turbulence Element scales the noise source over the area it affects,
+ and uses the curl of that source to generate force vectors.
+
+ Turbulence requires a fixed size. Unlike other affectors, a 0x0 Turbulence element
+ will affect no particles.
+
+ The source should be relatively smooth black and white noise, such as perlin noise.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Turbulence::strength
+
+ The magnitude of the velocity vector at any point varies between zero and
+ the square root of two. It will then be multiplied by strength to get the
+ velocity per second for the particles affected by the turbulence.
+*/
+/*!
+ \qmlproperty url QtQuick.Particles2::Turbulence::noiseSource
+
+ The source image to generate the turbulence from. It will be scaled to the size of the element,
+ so equal or larger sizes will give better results. Tweaking this image is the only way to tweak
+ behavior such as where vortices are or how many exist.
+
+ The source should be a relatively smooth black and white noise image, such as perlin noise.
+ A default image will be used if none is provided.
+*/
+
+QQuickTurbulenceAffector::QQuickTurbulenceAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent),
+ m_strength(10), m_lastT(0), m_gridSize(0), m_field(0), m_vectorField(0), m_inited(false)
+{
+}
+
+void QQuickTurbulenceAffector::geometryChanged(const QRectF &, const QRectF &)
+{
+ initializeGrid();
+}
+
+QQuickTurbulenceAffector::~QQuickTurbulenceAffector()
+{
+ if (m_field) {
+ for (int i=0; i<m_gridSize; i++)
+ free(m_field[i]);
+ free(m_field);
+ }
+ if (m_vectorField) {
+ for (int i=0; i<m_gridSize; i++)
+ free(m_vectorField[i]);
+ free(m_vectorField);
+ }
+}
+
+void QQuickTurbulenceAffector::initializeGrid()
+{
+ if (!m_inited)
+ return;
+
+ int arg = qMax(width(), height());
+ if (m_gridSize != arg) {
+ if (m_field){ //deallocate and then reallocate grid
+ for (int i=0; i<m_gridSize; i++)
+ free(m_field[i]);
+ free(m_field);
+ m_system = 0;
+ }
+ if (m_vectorField) {
+ for (int i=0; i<m_gridSize; i++)
+ free(m_vectorField[i]);
+ free(m_vectorField);
+ }
+ m_gridSize = arg;
+ }
+
+ m_field = (qreal**)malloc(m_gridSize * sizeof(qreal*));
+ for (int i=0; i<m_gridSize; i++)
+ m_field[i] = (qreal*)malloc(m_gridSize * sizeof(qreal));
+ m_vectorField = (QPointF**)malloc(m_gridSize * sizeof(QPointF*));
+ for (int i=0; i<m_gridSize; i++)
+ m_vectorField[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF));
+
+ QImage image;
+ if (!m_noiseSource.isEmpty())
+ image = QImage(m_noiseSource.toLocalFile()).scaled(QSize(m_gridSize, m_gridSize));
+ if (image.isNull())
+ image = QImage(QStringLiteral(":particleresources/noise.png")).scaled(QSize(m_gridSize, m_gridSize));
+
+ for (int i=0; i<m_gridSize; i++)
+ for (int j=0; j<m_gridSize; j++)
+ m_field[i][j] = qRed(image.pixel(QPoint(i,j)));//Red as proxy for Value
+ for (int i=0; i<m_gridSize; i++){
+ for (int j=0; j<m_gridSize; j++){
+ m_vectorField[i][j].setX(boundsRespectingField(i,j) - boundsRespectingField(i,j-1));
+ m_vectorField[i][j].setY(boundsRespectingField(i-1,j) - boundsRespectingField(i,j));
+ }
+ }
+}
+
+qreal QQuickTurbulenceAffector::boundsRespectingField(int x, int y)
+{
+ if (x < 0)
+ x = 0;
+ if (x >= m_gridSize)
+ x = m_gridSize - 1;
+ if (y < 0)
+ y = 0;
+ if (y >= m_gridSize)
+ y = m_gridSize - 1;
+ return m_field[x][y];
+}
+
+void QQuickTurbulenceAffector::ensureInit()
+{
+ if (m_inited)
+ return;
+ m_inited = true;
+ initializeGrid();
+}
+
+void QQuickTurbulenceAffector::affectSystem(qreal dt)
+{
+ if (!m_system || !m_enabled)
+ return;
+ ensureInit();
+ if (!m_gridSize)
+ return;
+
+ updateOffsets();//### Needed if an ancestor is transformed.
+
+ QRect boundsRect(0,0,m_gridSize,m_gridSize);
+ foreach (QQuickParticleGroupData *gd, m_system->groupData){
+ if (!activeGroup(m_system->groupData.key(gd)))
+ continue;
+ foreach (QQuickParticleData *d, gd->data){
+ if (!shouldAffect(d))
+ continue;
+ QPoint pos = (QPointF(d->curX(), d->curY()) - m_offset).toPoint();
+ if (!boundsRect.contains(pos,true))//Need to redo bounds checking due to quantization.
+ continue;
+ qreal fx = 0.0;
+ qreal fy = 0.0;
+ fx += m_vectorField[pos.x()][pos.y()].x() * m_strength;
+ fy += m_vectorField[pos.x()][pos.y()].y() * m_strength;
+ if (fx || fy){
+ d->setInstantaneousVX(d->curVX()+ fx * dt);
+ d->setInstantaneousVY(d->curVY()+ fy * dt);
+ postAffect(d);
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickturbulence_p.h b/src/particles/qquickturbulence_p.h
new file mode 100644
index 0000000000..cd5535c387
--- /dev/null
+++ b/src/particles/qquickturbulence_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TURBULENCEAFFECTOR_H
+#define TURBULENCEAFFECTOR_H
+#include "qquickparticleaffector_p.h"
+#include <QQmlListProperty>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticlePainter;
+
+class QQuickTurbulenceAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged)
+ Q_PROPERTY(QUrl noiseSource READ noiseSource WRITE setNoiseSource NOTIFY noiseSourceChanged)
+ public:
+ explicit QQuickTurbulenceAffector(QQuickItem *parent = 0);
+ ~QQuickTurbulenceAffector();
+ virtual void affectSystem(qreal dt);
+
+ qreal strength() const
+ {
+ return m_strength;
+ }
+
+ QUrl noiseSource() const
+ {
+ return m_noiseSource;
+ }
+signals:
+
+ void strengthChanged(qreal arg);
+
+ void noiseSourceChanged(QUrl arg);
+
+public slots:
+
+ void setStrength(qreal arg)
+ {
+ if (m_strength != arg) {
+ m_strength = arg;
+ emit strengthChanged(arg);
+ }
+ }
+
+ void setNoiseSource(QUrl arg)
+ {
+ if (m_noiseSource != arg) {
+ m_noiseSource = arg;
+ emit noiseSourceChanged(arg);
+ initializeGrid();
+ }
+ }
+
+protected:
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+private:
+ void ensureInit();
+ void mapUpdate();
+ void initializeGrid();
+ qreal boundsRespectingField(int x, int y);
+ qreal m_strength;
+ qreal m_lastT;
+ int m_gridSize;
+ qreal** m_field;
+ QPointF** m_vectorField;
+ bool m_inited;
+ QUrl m_noiseSource;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // TURBULENCEAFFECTOR_H
diff --git a/src/particles/qquickv8particledata.cpp b/src/particles/qquickv8particledata.cpp
new file mode 100644
index 0000000000..8e50e41091
--- /dev/null
+++ b/src/particles/qquickv8particledata.cpp
@@ -0,0 +1,505 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickv8particledata_p.h"
+#include "qquickparticlesystem_p.h"//for QQuickParticleData
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass Particle
+ \inqmlmodule QtQuick.Particles 2
+ \brief Particle elements can be manipulated in custom emitters and affectors.
+
+ Particle elements are always managed internally by the ParticleSystem and cannot be created in QML.
+ However, sometimes they are exposed via signals so as to allow arbitrary changes to the particle state
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialX
+ The x coordinate of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialVX
+ The x velocity of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialAX
+ The x acceleration of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialY
+ The y coordinate of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialVY
+ The y velocity of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::initialAY
+ The y acceleration of the particle at the beginning of its lifetime.
+
+ The method of simulation prefers to have the initial values changed, rather
+ than determining and changing the value at a given time. Change initial
+ values in CustomEmitters instead of the current values.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::x
+ The current x coordinate of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::vx
+ The current x velocity of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::ax
+ The current x acceleration of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::y
+ The current y coordinate of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::vy
+ The current y velocity of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::ay
+ The current y acceleration of the particle.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::t
+ The time, in seconds since the beginning of the simulation, that the particle was born.
+*/
+
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::startSize
+ The size in pixels that the particle image is at the start
+ of its life.
+*/
+
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::endSize
+ The size in pixels that the particle image is at the end
+ of its life. If this value is less than 0, then it is
+ disregarded and the particle will have its startSize for the
+ entire lifetime.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::lifeSpan
+ The time in seconds that the particle will live for.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::rotation
+ Degrees clockwise that the particle image is rotated at
+ the beginning of its life.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::rotationSpeed
+ Degrees clockwise per second that the particle image is rotated at while alive.
+*/
+/*!
+ \qmlproperty bool QtQuick.Particles2::Particle::autoRotate
+ If autoRotate is true, then the particle's rotation will be
+ set so that it faces the direction of travel, plus any
+ rotation from the rotation or rotationSpeed properties.
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Particles2::Particle::update
+
+ Inside an Affector, the changes made to the particle will only be
+ applied if update is set to true.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::xDeformationVectorX
+
+ The x component of the deformation vector along the X axis. ImageParticle
+ can draw particles across non-square shapes. It will draw the texture rectangle
+ across the parallelogram drawn with the x and y deformation vectors.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::yDeformationVectorX
+
+ The y component of the deformation vector along the X axis. ImageParticle
+ can draw particles across non-square shapes. It will draw the texture rectangle
+ across the parallelogram drawn with the x and y deformation vectors.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::xDeformationVectorY
+
+ The x component of the deformation vector along the X axis. ImageParticle
+ can draw particles across non-square shapes. It will draw the texture rectangle
+ across the parallelogram drawn with the x and y deformation vectors.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::yDeformationVectorY
+
+ The y component of the deformation vector along the Y axis. ImageParticle
+ can draw particles across non-square shapes. It will draw the texture rectangle
+ across the parallelogram drawn with the x and y deformation vectors.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::red
+
+ ImageParticle can draw colorized particles. When it does so, red is used
+ as the red channel of the color applied to the source image.
+
+ Values are from 0.0 to 1.0.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::green
+
+ ImageParticle can draw colorized particles. When it does so, green is used
+ as the green channel of the color applied to the source image.
+
+ Values are from 0.0 to 1.0.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::blue
+
+ ImageParticle can draw colorized particles. When it does so, blue is used
+ as the blue channel of the color applied to the source image.
+
+ Values are from 0.0 to 1.0.
+*/
+
+/*!
+ \qmlproperty real QtQuick.Particles2::Particle::alpha
+
+ ImageParticle can draw colorized particles. When it does so, alpha is used
+ as the alpha channel of the color applied to the source image.
+
+ Values are from 0.0 to 1.0.
+*/
+/*!
+ \qmlmethod real QtQuick.Particles2::Particle::lifeLeft
+ The time in seconds that the particle has left to live at
+ the current point in time.
+*/
+/*!
+ \qmlmethod real QtQuick.Particles2::Particle::currentSize
+ The currentSize of the particle, interpolating between startSize and endSize based on the currentTime.
+*/
+
+
+
+//### Particle data handles are not locked to within certain scopes like QQuickContext2D, but there's no way to reload either...
+class QV8ParticleDataResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(ParticleDataType)
+public:
+ QV8ParticleDataResource(QV8Engine *e) : QV8ObjectResource(e) {}
+ QQuickParticleData* datum;//TODO: Guard needed?
+};
+
+class QV8ParticleDataDeletable : public QV8Engine::Deletable
+{
+public:
+ QV8ParticleDataDeletable(QV8Engine *engine);
+ ~QV8ParticleDataDeletable();
+
+ v8::Persistent<v8::Function> constructor;
+};
+
+static v8::Handle<v8::Value> particleData_discard(const v8::Arguments &args)
+{
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+
+ if (!r || !r->datum)
+ V8THROW_ERROR("Not a valid ParticleData object");
+
+ r->datum->lifeSpan = 0; //Don't kill(), because it could still be in the middle of being created
+ return v8::Undefined();
+}
+
+static v8::Handle<v8::Value> particleData_lifeLeft(const v8::Arguments &args)
+{
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+ if (!r || !r->datum)
+ V8THROW_ERROR("Not a valid ParticleData object");
+
+ return v8::Number::New(r->datum->lifeLeft());
+}
+
+static v8::Handle<v8::Value> particleData_curSize(const v8::Arguments &args)
+{
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+ if (!r || !r->datum)
+ V8THROW_ERROR("Not a valid ParticleData object");
+
+ return v8::Number::New(r->datum->curSize());
+}
+#define COLOR_GETTER_AND_SETTER(VAR, NAME) static v8::Handle<v8::Value> particleData_get_ ## NAME (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+ if (!r || !r->datum) \
+ V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+ return v8::Number::New((r->datum->color. VAR )/255.0);\
+}\
+\
+static void particleData_set_ ## NAME (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+ if (!r || !r->datum)\
+ V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+ r->datum->color. VAR = qMin(255, qMax(0, (int)floor(value->NumberValue() * 255.0)));\
+}
+
+
+#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static v8::Handle<v8::Value> particleData_get_ ## VARIABLE (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+ if (!r || !r->datum) \
+ V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+ return v8::Boolean::New(r->datum-> VARIABLE);\
+}\
+\
+static void particleData_set_ ## VARIABLE (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+ if (!r || !r->datum)\
+ V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+ r->datum-> VARIABLE = value->BooleanValue() ? 1.0 : 0.0;\
+}
+
+#define FLOAT_GETTER_AND_SETTER(VARIABLE) static v8::Handle<v8::Value> particleData_get_ ## VARIABLE (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+ if (!r || !r->datum) \
+ V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+ return v8::Number::New(r->datum-> VARIABLE);\
+}\
+\
+static void particleData_set_ ## VARIABLE (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+ if (!r || !r->datum)\
+ V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+ r->datum-> VARIABLE = value->NumberValue();\
+}
+
+#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static v8::Handle<v8::Value> particleData_get_ ## VARIABLE (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+ if (!r || !r->datum) \
+ V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+ return v8::Number::New(r->datum-> GETTER ());\
+}\
+\
+static void particleData_set_ ## VARIABLE (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+ QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+ if (!r || !r->datum)\
+ V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+ r->datum-> SETTER ( value->NumberValue() );\
+}
+
+#define REGISTER_ACCESSOR(FT, ENGINE, VARIABLE, NAME) FT ->PrototypeTemplate()->SetAccessor( v8::String::New( #NAME ), particleData_get_ ## VARIABLE , particleData_set_ ## VARIABLE , v8::External::Wrap(ENGINE))
+
+COLOR_GETTER_AND_SETTER(r, red)
+COLOR_GETTER_AND_SETTER(g, green)
+COLOR_GETTER_AND_SETTER(b, blue)
+COLOR_GETTER_AND_SETTER(a, alpha)
+SEMIBOOL_GETTER_AND_SETTER(autoRotate)
+SEMIBOOL_GETTER_AND_SETTER(update)
+FLOAT_GETTER_AND_SETTER(x)
+FLOAT_GETTER_AND_SETTER(y)
+FLOAT_GETTER_AND_SETTER(t)
+FLOAT_GETTER_AND_SETTER(lifeSpan)
+FLOAT_GETTER_AND_SETTER(size)
+FLOAT_GETTER_AND_SETTER(endSize)
+FLOAT_GETTER_AND_SETTER(vx)
+FLOAT_GETTER_AND_SETTER(vy)
+FLOAT_GETTER_AND_SETTER(ax)
+FLOAT_GETTER_AND_SETTER(ay)
+FLOAT_GETTER_AND_SETTER(xx)
+FLOAT_GETTER_AND_SETTER(xy)
+FLOAT_GETTER_AND_SETTER(yx)
+FLOAT_GETTER_AND_SETTER(yy)
+FLOAT_GETTER_AND_SETTER(rotation)
+FLOAT_GETTER_AND_SETTER(rotationSpeed)
+FLOAT_GETTER_AND_SETTER(animIdx)
+FLOAT_GETTER_AND_SETTER(frameDuration)
+FLOAT_GETTER_AND_SETTER(frameAt)
+FLOAT_GETTER_AND_SETTER(frameCount)
+FLOAT_GETTER_AND_SETTER(animT)
+FLOAT_GETTER_AND_SETTER(r)
+FAKE_FLOAT_GETTER_AND_SETTER(curX, curX, setInstantaneousX)
+FAKE_FLOAT_GETTER_AND_SETTER(curVX, curVX, setInstantaneousVX)
+FAKE_FLOAT_GETTER_AND_SETTER(curAX, curAX, setInstantaneousAX)
+FAKE_FLOAT_GETTER_AND_SETTER(curY, curY, setInstantaneousY)
+FAKE_FLOAT_GETTER_AND_SETTER(curVY, curVY, setInstantaneousVY)
+FAKE_FLOAT_GETTER_AND_SETTER(curAY, curAY, setInstantaneousAY)
+
+QV8ParticleDataDeletable::QV8ParticleDataDeletable(QV8Engine *engine)
+{
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(engine->context());
+
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->PrototypeTemplate()->Set(v8::String::New("discard"), V8FUNCTION(particleData_discard, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("lifeLeft"), V8FUNCTION(particleData_lifeLeft, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("currentSize"), V8FUNCTION(particleData_curSize, engine));
+ REGISTER_ACCESSOR(ft, engine, x, initialX);
+ REGISTER_ACCESSOR(ft, engine, y, initialY);
+ REGISTER_ACCESSOR(ft, engine, t, t);
+ REGISTER_ACCESSOR(ft, engine, lifeSpan, lifeSpan);
+ REGISTER_ACCESSOR(ft, engine, size, startSize);
+ REGISTER_ACCESSOR(ft, engine, endSize, endSize);
+ REGISTER_ACCESSOR(ft, engine, vx, initialVX);
+ REGISTER_ACCESSOR(ft, engine, vy, initialVY);
+ REGISTER_ACCESSOR(ft, engine, ax, initialAX);
+ REGISTER_ACCESSOR(ft, engine, ay, initialAY);
+ REGISTER_ACCESSOR(ft, engine, xx, xDeformationVectorX);
+ REGISTER_ACCESSOR(ft, engine, xy, xDeformationVectorY);
+ REGISTER_ACCESSOR(ft, engine, yx, yDeformationVectorX);
+ REGISTER_ACCESSOR(ft, engine, yy, yDeformationVectorY);
+ REGISTER_ACCESSOR(ft, engine, rotation, rotation);
+ REGISTER_ACCESSOR(ft, engine, rotationSpeed, rotationSpeed);
+ REGISTER_ACCESSOR(ft, engine, autoRotate, autoRotate);
+ REGISTER_ACCESSOR(ft, engine, animIdx, animationIndex);
+ REGISTER_ACCESSOR(ft, engine, frameDuration, frameDuration);
+ REGISTER_ACCESSOR(ft, engine, frameAt, frameAt);
+ REGISTER_ACCESSOR(ft, engine, frameCount, frameCount);
+ REGISTER_ACCESSOR(ft, engine, animT, animationT);
+ REGISTER_ACCESSOR(ft, engine, r, r);
+ REGISTER_ACCESSOR(ft, engine, update, update);
+ REGISTER_ACCESSOR(ft, engine, curX, x);
+ REGISTER_ACCESSOR(ft, engine, curVX, vx);
+ REGISTER_ACCESSOR(ft, engine, curAX, ax);
+ REGISTER_ACCESSOR(ft, engine, curY, y);
+ REGISTER_ACCESSOR(ft, engine, curVY, vy);
+ REGISTER_ACCESSOR(ft, engine, curAY, ay);
+ REGISTER_ACCESSOR(ft, engine, red, red);
+ REGISTER_ACCESSOR(ft, engine, green, green);
+ REGISTER_ACCESSOR(ft, engine, blue, blue);
+ REGISTER_ACCESSOR(ft, engine, alpha, alpha);
+
+ constructor = qPersistentNew(ft->GetFunction());
+}
+
+QV8ParticleDataDeletable::~QV8ParticleDataDeletable()
+{
+ qPersistentDispose(constructor);
+}
+
+V8_DEFINE_EXTENSION(QV8ParticleDataDeletable, particleV8Data);
+
+
+QQuickV8ParticleData::QQuickV8ParticleData(QV8Engine* engine, QQuickParticleData* datum)
+{
+ if (!engine || !datum)
+ return;
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(engine->context());
+
+ QV8ParticleDataDeletable *d = particleV8Data(engine);
+ m_v8Value = qPersistentNew(d->constructor->NewInstance());
+ QV8ParticleDataResource *r = new QV8ParticleDataResource(engine);
+ r->datum = datum;
+ m_v8Value->SetExternalResource(r);
+}
+
+QQuickV8ParticleData::~QQuickV8ParticleData()
+{
+ qPersistentDispose(m_v8Value);
+}
+
+QQmlV8Handle QQuickV8ParticleData::v8Value()
+{
+ return QQmlV8Handle::fromHandle(m_v8Value);
+}
+
+QT_END_NAMESPACE
diff --git a/src/particles/qquickv8particledata_p.h b/src/particles/qquickv8particledata_p.h
new file mode 100644
index 0000000000..21ec6458c1
--- /dev/null
+++ b/src/particles/qquickv8particledata_p.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQuickV8PARTICLEDATA_H
+#define QQuickV8PARTICLEDATA_H
+
+#include <private/qv8engine_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickParticleData;
+class QQuickV8ParticleData {
+public:
+ QQuickV8ParticleData(QV8Engine*,QQuickParticleData*);
+ ~QQuickV8ParticleData();
+ QQmlV8Handle v8Value();
+private:
+ v8::Persistent<v8::Object> m_v8Value;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif
diff --git a/src/particles/qquickwander.cpp b/src/particles/qquickwander.cpp
new file mode 100644
index 0000000000..0f9a5f069b
--- /dev/null
+++ b/src/particles/qquickwander.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickwander_p.h"
+#include "qquickparticlesystem_p.h"//for ParticlesVertices
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Wander QQuickWanderAffector
+ \inqmlmodule QtQuick.Particles 2
+ \inherits Affector
+ \brief The Wander affector allows particles to randomly vary their trajectory.
+
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Wander::pace
+
+ Maximum attribute change per second.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Wander::xVariance
+
+ Maximum attribute x value (as a result of Wander).
+
+ If unset, Wander will not affect x values.
+*/
+/*!
+ \qmlproperty real QtQuick.Particles2::Wander::yVariance
+
+ Maximum attribute y value (as a result of Wander).
+
+ If unset, Wander will not affect y values.
+*/
+/*!
+ \qmlproperty AffectableParameter QtQuick.Particles2::Wander::affectedParameter
+
+ What attribute of particles is directly affected.
+ \list
+ \li PointAttractor.Position
+ \li PointAttractor.Velocity
+ \li PointAttractor.Acceleration
+ \endlist
+*/
+
+QQuickWanderAffector::QQuickWanderAffector(QQuickItem *parent) :
+ QQuickParticleAffector(parent), m_xVariance(0), m_yVariance(0), m_pace(0)
+ , m_affectedParameter(Velocity)
+{
+ m_needsReset = true;
+}
+
+QQuickWanderAffector::~QQuickWanderAffector()
+{
+ for (QHash<int, WanderData*>::const_iterator iter=m_wanderData.constBegin();
+ iter != m_wanderData.constEnd(); iter++)
+ delete (*iter);
+}
+
+WanderData* QQuickWanderAffector::getData(int idx)
+{
+ if (m_wanderData.contains(idx))
+ return m_wanderData[idx];
+ WanderData* d = new WanderData;
+ d->x_vel = 0;
+ d->y_vel = 0;
+ d->x_peak = m_xVariance;
+ d->y_peak = m_yVariance;
+ d->x_var = m_pace * qreal(qrand()) / RAND_MAX;
+ d->y_var = m_pace * qreal(qrand()) / RAND_MAX;
+
+ m_wanderData.insert(idx, d);
+ return d;
+}
+
+// TODO: see below
+//void QQuickWanderAffector::reset(int systemIdx)
+//{
+// if (m_wanderData.contains(systemIdx))
+// delete m_wanderData[systemIdx];
+// m_wanderData.remove(systemIdx);
+//}
+
+bool QQuickWanderAffector::affectParticle(QQuickParticleData* data, qreal dt)
+{
+ /*TODO: Add a mode which does basically this - picking a direction, going in it (random speed) and then going back
+ WanderData* d = getData(data->systemIndex);
+ if (m_xVariance != 0.) {
+ if ((d->x_vel > d->x_peak && d->x_var > 0.0) || (d->x_vel < -d->x_peak && d->x_var < 0.0)) {
+ d->x_var = -d->x_var;
+ d->x_peak = m_xVariance + m_xVariance * qreal(qrand()) / RAND_MAX;
+ }
+ d->x_vel += d->x_var * dt;
+ }
+ qreal dx = dt * d->x_vel;
+
+ if (m_yVariance != 0.) {
+ if ((d->y_vel > d->y_peak && d->y_var > 0.0) || (d->y_vel < -d->y_peak && d->y_var < 0.0)) {
+ d->y_var = -d->y_var;
+ d->y_peak = m_yVariance + m_yVariance * qreal(qrand()) / RAND_MAX;
+ }
+ d->y_vel += d->y_var * dt;
+ }
+ qreal dy = dt * d->x_vel;
+
+ //### Should we be amending vel instead?
+ ParticleVertex* p = &(data->pv);
+ p->x += dx;
+
+ p->y += dy;
+ return true;
+ */
+ qreal dx = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1);
+ qreal dy = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1);
+ qreal newX, newY;
+ switch (m_affectedParameter){
+ case Position:
+ newX = data->curX() + dx;
+ if (m_xVariance > qAbs(newX) )
+ data->x += dx;
+ newY = data->curY() + dy;
+ if (m_yVariance > qAbs(newY) )
+ data->y += dy;
+ break;
+ default:
+ case Velocity:
+ newX = data->curVX() + dx;
+ if (m_xVariance > qAbs(newX) )
+ data->setInstantaneousVX(newX);
+ newY = data->curVY() + dy;
+ if (m_yVariance > qAbs(newY) )
+ data->setInstantaneousVY(newY);
+ break;
+ case Acceleration:
+ newX = data->ax + dx;
+ if (m_xVariance > qAbs(newX) )
+ data->setInstantaneousAX(newX);
+ newY = data->ay + dy;
+ if (m_yVariance > qAbs(newY) )
+ data->setInstantaneousAY(newY);
+ break;
+ }
+ return true;
+}
+QT_END_NAMESPACE
diff --git a/src/particles/qquickwander_p.h b/src/particles/qquickwander_p.h
new file mode 100644
index 0000000000..bb418f7912
--- /dev/null
+++ b/src/particles/qquickwander_p.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WANDERAFFECTOR_H
+#define WANDERAFFECTOR_H
+#include <QHash>
+#include "qquickparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+struct WanderData{
+ qreal x_vel;
+ qreal y_vel;
+ qreal x_peak;
+ qreal x_var;
+ qreal y_peak;
+ qreal y_var;
+};
+
+class QQuickWanderAffector : public QQuickParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged)
+ Q_PROPERTY(qreal xVariance READ xVariance WRITE setXVariance NOTIFY xVarianceChanged)
+ Q_PROPERTY(qreal yVariance READ yVariance WRITE setYVariance NOTIFY yVarianceChanged)
+ Q_PROPERTY(AffectableParameters affectedParameter READ affectedParameter WRITE setAffectedParameter NOTIFY affectedParameterChanged)
+ Q_ENUMS(AffectableParameters)
+
+public:
+ enum AffectableParameters {
+ Position,
+ Velocity,
+ Acceleration
+ };
+
+ explicit QQuickWanderAffector(QQuickItem *parent = 0);
+ ~QQuickWanderAffector();
+// virtual void reset(int systemIdx);
+
+ qreal xVariance() const
+ {
+ return m_xVariance;
+ }
+
+ qreal yVariance() const
+ {
+ return m_yVariance;
+ }
+
+ qreal pace() const
+ {
+ return m_pace;
+ }
+
+ AffectableParameters affectedParameter() const
+ {
+ return m_affectedParameter;
+ }
+
+protected:
+ virtual bool affectParticle(QQuickParticleData *d, qreal dt);
+signals:
+
+ void xVarianceChanged(qreal arg);
+
+ void yVarianceChanged(qreal arg);
+
+ void paceChanged(qreal arg);
+
+
+ void affectedParameterChanged(AffectableParameters arg);
+
+public slots:
+void setXVariance(qreal arg)
+{
+ if (m_xVariance != arg) {
+ m_xVariance = arg;
+ emit xVarianceChanged(arg);
+ }
+}
+
+void setYVariance(qreal arg)
+{
+ if (m_yVariance != arg) {
+ m_yVariance = arg;
+ emit yVarianceChanged(arg);
+ }
+}
+
+void setPace(qreal arg)
+{
+ if (m_pace != arg) {
+ m_pace = arg;
+ emit paceChanged(arg);
+ }
+}
+
+
+void setAffectedParameter(AffectableParameters arg)
+{
+ if (m_affectedParameter != arg) {
+ m_affectedParameter = arg;
+ emit affectedParameterChanged(arg);
+ }
+}
+
+private:
+ WanderData* getData(int idx);
+ QHash<int, WanderData*> m_wanderData;
+ qreal m_xVariance;
+ qreal m_yVariance;
+ qreal m_pace;
+ AffectableParameters m_affectedParameter;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // WANDERAFFECTOR_H
diff --git a/src/particles/qtquickparticlesglobal_p.h b/src/particles/qtquickparticlesglobal_p.h
new file mode 100644
index 0000000000..d7814f759d
--- /dev/null
+++ b/src/particles/qtquickparticlesglobal_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQuickParticles module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQUICKPARTICLESGLOBAL_P_H
+#define QTQUICKPARTICLESGLOBAL_P_H
+
+#include <QtCore/qglobal.h>
+
+// We only have private exports from this library
+
+#if defined(Q_OS_WIN)
+# if defined(QT_MAKEDLL) /* create a Qt DLL library */
+# if defined(QT_BUILD_QUICKPARTICLES_LIB)
+# define Q_QUICKPARTICLES_PRIVATE_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QUICKPARTICLES_PRIVATE_EXPORT Q_DECL_IMPORT
+# endif
+# elif defined(QT_DLL) /* use a Qt DLL library */
+# define Q_QUICKPARTICLES_PRIVATE_EXPORT Q_DECL_IMPORT
+# endif
+#endif
+
+#if !defined(Q_QUICKPARTICLES_PRIVATE_EXPORT)
+# if defined(QT_SHARED)
+# define Q_QUICKPARTICLES_PRIVATE_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QUICKPARTICLES_PRIVATE_EXPORT
+# endif
+#endif
+
+#endif // QTQUICKPARTICLESGLOBAL_P_H