aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demos/declarative/flickr/content/StreamView.qml1
-rw-r--r--examples/declarative/particles/custom/delegates.qml89
-rw-r--r--examples/declarative/particles/exampleslauncher.qml6
-rw-r--r--examples/declarative/particles/launcherContent/icons/combustion.pngbin0 -> 7173 bytes
-rw-r--r--examples/declarative/particles/launcherContent/icons/delegates.pngbin0 -> 1892 bytes
-rw-r--r--examples/declarative/particles/launcherContent/icons/fireworks.pngbin0 -> 16139 bytes
-rw-r--r--examples/declarative/particles/launcherContent/icons/shader.pngbin0 -> 22294 bytes
-rw-r--r--examples/declarative/particles/modelparticles/package.qml2
-rw-r--r--examples/declarative/particles/modelparticles/stream.qml1
-rw-r--r--examples/declarative/particles/snow/snow.qml1
-rw-r--r--examples/declarative/particles/trails/combustion.qml199
-rw-r--r--examples/declarative/particles/trails/content/matchmask.pngbin0 -> 2369 bytes
-rw-r--r--examples/declarative/particles/trails/dynamicemitters.qml1
-rw-r--r--examples/declarative/particles/trails/fireworks.qml (renamed from examples/declarative/particles/custom/fireworks.qml)86
-rw-r--r--src/declarative/items/qsgsprite.cpp16
-rw-r--r--src/declarative/items/qsgsprite_p.h10
-rw-r--r--src/declarative/items/qsgspriteengine.cpp174
-rw-r--r--src/declarative/items/qsgspriteengine_p.h7
-rw-r--r--src/declarative/particles/particles.pri11
-rw-r--r--src/declarative/particles/qsgcumulativedirection.cpp62
-rw-r--r--src/declarative/particles/qsgcumulativedirection_p.h64
-rw-r--r--src/declarative/particles/qsgcustomparticle.cpp183
-rw-r--r--src/declarative/particles/qsgcustomparticle_p.h9
-rw-r--r--src/declarative/particles/qsgemitter.cpp21
-rw-r--r--src/declarative/particles/qsgfollowemitter.cpp36
-rw-r--r--src/declarative/particles/qsgfriction.cpp2
-rw-r--r--src/declarative/particles/qsggravity.cpp4
-rw-r--r--src/declarative/particles/qsgimageparticle.cpp485
-rw-r--r--src/declarative/particles/qsgimageparticle_p.h16
-rw-r--r--src/declarative/particles/qsgitemparticle.cpp111
-rw-r--r--src/declarative/particles/qsgitemparticle_p.h8
-rw-r--r--src/declarative/particles/qsgkill.cpp3
-rw-r--r--src/declarative/particles/qsglineextruder.cpp6
-rw-r--r--src/declarative/particles/qsgmaskextruder.cpp16
-rw-r--r--src/declarative/particles/qsgmodelparticle.cpp140
-rw-r--r--src/declarative/particles/qsgmodelparticle_p.h10
-rw-r--r--src/declarative/particles/qsgparticleaffector.cpp72
-rw-r--r--src/declarative/particles/qsgparticleaffector_p.h22
-rw-r--r--src/declarative/particles/qsgparticleemitter.cpp24
-rw-r--r--src/declarative/particles/qsgparticleextruder.cpp4
-rw-r--r--src/declarative/particles/qsgparticlepainter.cpp95
-rw-r--r--src/declarative/particles/qsgparticlepainter_p.h23
-rw-r--r--src/declarative/particles/qsgparticlesmodule.cpp7
-rw-r--r--src/declarative/particles/qsgparticlesystem.cpp788
-rw-r--r--src/declarative/particles/qsgparticlesystem_p.h251
-rw-r--r--src/declarative/particles/qsgpointattractor.cpp6
-rw-r--r--src/declarative/particles/qsgspritegoal.cpp48
-rw-r--r--src/declarative/particles/qsgspritegoal_p.h19
-rw-r--r--src/declarative/particles/qsgtargetaffector.cpp92
-rw-r--r--src/declarative/particles/qsgtargetaffector_p.h168
-rw-r--r--src/declarative/particles/qsgtargeteddirection.cpp6
-rw-r--r--src/declarative/particles/qsgturbulence.cpp38
-rw-r--r--src/declarative/particles/qsgwander.cpp20
53 files changed, 2365 insertions, 1098 deletions
diff --git a/demos/declarative/flickr/content/StreamView.qml b/demos/declarative/flickr/content/StreamView.qml
index d7b608a391..e2c65e22fe 100644
--- a/demos/declarative/flickr/content/StreamView.qml
+++ b/demos/declarative/flickr/content/StreamView.qml
@@ -50,7 +50,6 @@ Item{
ParticleSystem{
id: sys
anchors.fill:parent
- overwrite: false
}
ModelParticle{
id: mp
diff --git a/examples/declarative/particles/custom/delegates.qml b/examples/declarative/particles/custom/delegates.qml
new file mode 100644
index 0000000000..4b01c66396
--- /dev/null
+++ b/examples/declarative/particles/custom/delegates.qml
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Particles 2.0
+
+Rectangle{
+ id: root;
+ width: 360
+ height: 600
+ function newPithySaying(){
+ switch (Math.floor(Math.random()*16)){
+ case 0: return "Hello World";
+ case 1: return "G'day Mate";
+ case 2: return "Code Less";
+ case 3: return "Create More";
+ case 4: return "Deploy Everywhere";
+ case 5: return "Qt Meta-object Language";
+ case 6: return "Qt Magic Language";
+ case 7: return "Fluid UIs";
+ case 8: return "Touchable";
+ case 9: return "How's it going?";
+ case 10: return "Do you like text?";
+ case 11: return "Enjoy!";
+ case 12: return "ERROR: Out of pith";
+ case 13: return "Punctuation Failure";
+ case 14: return "I can go faster";
+ case 15: return "I can go slower";
+ default: return "OMGWTFBBQ";
+ }
+ }
+ color: "black"
+ ParticleSystem{
+ anchors.fill: parent
+ id: syssy
+ Emitter{
+ anchors.centerIn: parent
+ emitRate: 1
+ lifeSpan: 4800
+ lifeSpanVariation: 1600
+ speed: AngledDirection{angleVariation: 360; magnitude: 40; magnitudeVariation: 20}
+ }
+ ItemParticle{
+ delegate: Text{
+ text: root.newPithySaying();
+ color: "white"
+ font.pixelSize: 18
+ font.bold: true
+ }
+ }
+ }
+}
diff --git a/examples/declarative/particles/exampleslauncher.qml b/examples/declarative/particles/exampleslauncher.qml
index 354bcdf65a..54f57654c3 100644
--- a/examples/declarative/particles/exampleslauncher.qml
+++ b/examples/declarative/particles/exampleslauncher.qml
@@ -52,7 +52,7 @@ Rectangle{
id: shell
anchors.fill: parent
}
- VisualDataModel{//TODO: Transitions between modes
+ VisualDataModel{//TODO: Transitions
id: vdm
model: [
"../spaceexplorer/spaceexplorer.qml",
@@ -60,6 +60,8 @@ Rectangle{
"../asteroid/asteroid.qml",
"../asteroid/blackhole.qml",
"../custom/blurparticles.qml",
+ "../custom/shader.qml",
+ "../custom/delegates.qml",
"../modelparticles/bubbles.qml",
"../modelparticles/gridsplosion.qml",
"../modelparticles/package.qml",
@@ -81,6 +83,8 @@ Rectangle{
"../trails/layered.qml",
"../trails/shimmer.qml",
"../trails/turbulence.qml",
+ "../trails/combustion.qml",
+ "../trails/fireworks.qml",
"../../../../demos/declarative/samegame/samegame.qml",
"../../../../demos/declarative/plasmapatrol/plasmapatrol.qml",
"../../../../demos/declarative/flickr/flickr.qml"
diff --git a/examples/declarative/particles/launcherContent/icons/combustion.png b/examples/declarative/particles/launcherContent/icons/combustion.png
new file mode 100644
index 0000000000..69c6f64e54
--- /dev/null
+++ b/examples/declarative/particles/launcherContent/icons/combustion.png
Binary files differ
diff --git a/examples/declarative/particles/launcherContent/icons/delegates.png b/examples/declarative/particles/launcherContent/icons/delegates.png
new file mode 100644
index 0000000000..929414cd0e
--- /dev/null
+++ b/examples/declarative/particles/launcherContent/icons/delegates.png
Binary files differ
diff --git a/examples/declarative/particles/launcherContent/icons/fireworks.png b/examples/declarative/particles/launcherContent/icons/fireworks.png
new file mode 100644
index 0000000000..ec9f826495
--- /dev/null
+++ b/examples/declarative/particles/launcherContent/icons/fireworks.png
Binary files differ
diff --git a/examples/declarative/particles/launcherContent/icons/shader.png b/examples/declarative/particles/launcherContent/icons/shader.png
new file mode 100644
index 0000000000..7c6de498d5
--- /dev/null
+++ b/examples/declarative/particles/launcherContent/icons/shader.png
Binary files differ
diff --git a/examples/declarative/particles/modelparticles/package.qml b/examples/declarative/particles/modelparticles/package.qml
index 0aa8903270..d374a93446 100644
--- a/examples/declarative/particles/modelparticles/package.qml
+++ b/examples/declarative/particles/modelparticles/package.qml
@@ -83,7 +83,7 @@ Rectangle {
Emitter{
system: sys
width: 100
- x: 50
+ x: 250
speed: PointDirection{ y: 40 }
lifeSpan: 5000
emitRate: 1.6
diff --git a/examples/declarative/particles/modelparticles/stream.qml b/examples/declarative/particles/modelparticles/stream.qml
index 5c7a6f7fc4..15280f7f21 100644
--- a/examples/declarative/particles/modelparticles/stream.qml
+++ b/examples/declarative/particles/modelparticles/stream.qml
@@ -65,7 +65,6 @@ Item{
ParticleSystem{
id: sys;
running: true
- overwrite: false
startTime: 12000//Doesn't actually work with the loading time though...
}
Emitter{
diff --git a/examples/declarative/particles/snow/snow.qml b/examples/declarative/particles/snow/snow.qml
index ea2de17046..b988c53f4a 100644
--- a/examples/declarative/particles/snow/snow.qml
+++ b/examples/declarative/particles/snow/snow.qml
@@ -54,6 +54,7 @@ Rectangle{
source: "content/flake-01.png"
frames: 51
duration: 40
+ durationVariation: 8
}
}
Wander {
diff --git a/examples/declarative/particles/trails/combustion.qml b/examples/declarative/particles/trails/combustion.qml
new file mode 100644
index 0000000000..f244300524
--- /dev/null
+++ b/examples/declarative/particles/trails/combustion.qml
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Particles 2.0
+
+
+Rectangle {
+ id: root
+ width: 360
+ height: 600
+ color: "black"
+
+ property int score: 0
+ Text{
+ color: "white"
+ anchors.right: parent.right
+ text: score
+ }
+ ParticleSystem{
+ id: particles
+ anchors.fill: parent
+
+
+ particleStates:[
+ Sprite{
+ name: "unlit"
+ duration: 1000
+ to: {"lighting":1, "unlit":99}
+ ImageParticle{
+ source: "content/particleA.png"
+ colorVariation: 0.1
+ color: "#2060160f"
+ }
+ SpriteGoal{
+ collisionParticles: ["lit"]
+ goalState: "lighting"
+ jump: true
+ systemStates: true
+ }
+ },
+ Sprite{
+ name: "lighting"
+ duration: 100
+ to: {"lit":1}
+ },
+ Sprite{
+ name: "lit"
+ duration: 10000
+ onEntered: score++;
+ FollowEmitter{
+ id: fireballFlame
+ particle: "flame"
+
+ emitRatePerParticle: 48
+ lifeSpan: 200
+ emitWidth: 8
+ emitHeight: 8
+
+ size: 24
+ sizeVariation: 8
+ endSize: 4
+ }
+
+ FollowEmitter{
+ id: fireballSmoke
+ particle: "smoke"
+
+ emitRatePerParticle: 120
+ lifeSpan: 2000
+ emitWidth: 16
+ emitHeight: 16
+
+ speed: PointDirection{yVariation: 16; xVariation: 16}
+ acceleration: PointDirection{y: -16}
+
+ size: 24
+ sizeVariation: 8
+ endSize: 8
+ }
+ }
+ ]
+
+ ImageParticle{
+ id: smoke
+ anchors.fill: parent
+ particles: ["smoke"]
+ source: "content/particle.png"
+ colorVariation: 0
+ color: "#00111111"
+ }
+ ImageParticle{
+ id: pilot
+ anchors.fill: parent
+ particles: ["pilot"]
+ source: "content/particle.png"
+ redVariation: 0.01
+ blueVariation: 0.4
+ color: "#0010004f"
+ }
+ ImageParticle{
+ id: flame
+ anchors.fill: parent
+ particles: ["flame", "lit", "lighting"]
+ source: "content/particleA.png"
+ colorVariation: 0.1
+ color: "#00ff400f"
+ }
+
+ Emitter{
+ height: parent.height/2
+ emitRate: 4
+ lifeSpan: 4000//TODO: Infinite & kill zone
+ size: 24
+ sizeVariation: 4
+ speed: PointDirection{x:120; xVariation: 80; yVariation: 50}
+ acceleration: PointDirection{y:120}
+ particle: "unlit"
+ }
+
+ Emitter{
+ id: flamer
+ x: 100
+ y: 300
+ particle: "pilot"
+ emitRate: 80
+ lifeSpan: 600
+ size: 24
+ sizeVariation: 2
+ endSize: 0
+ speed: PointDirection{ y:-100; yVariation: 4; xVariation: 4 }
+ SpriteGoal{
+ particles: ["unlit"]
+ goalState: "lit"
+ jump: true
+ systemStates: true
+ system: particles
+ x: -15
+ y: -55
+ height: 75
+ width: 30
+ shape: MaskShape{source: "content/matchmask.png"}
+ }
+ }
+ //Click to enflame
+ SpriteGoal{//TODO: Aux emiiters in the state definition (which allows the occasional ball to spontaneously combust)
+ particles: ["unlit"]
+ goalState: "lighting"
+ jump: true
+ systemStates: true
+ active: ma.pressed
+ width: 18
+ height: 18
+ x: ma.mouseX - width/2
+ y: ma.mouseY - height/2
+ }
+ MouseArea{
+ id: ma
+ anchors.fill: parent
+ }
+ }
+}
diff --git a/examples/declarative/particles/trails/content/matchmask.png b/examples/declarative/particles/trails/content/matchmask.png
new file mode 100644
index 0000000000..e575875c55
--- /dev/null
+++ b/examples/declarative/particles/trails/content/matchmask.png
Binary files differ
diff --git a/examples/declarative/particles/trails/dynamicemitters.qml b/examples/declarative/particles/trails/dynamicemitters.qml
index f338c204db..dbf3f8fa82 100644
--- a/examples/declarative/particles/trails/dynamicemitters.qml
+++ b/examples/declarative/particles/trails/dynamicemitters.qml
@@ -114,6 +114,7 @@ Rectangle{
obj.targetX = Math.random() * 240 - 120 + obj.x
obj.targetY = Math.random() * 240 - 120 + obj.y
obj.life = Math.round(Math.random() * 2400) + 200
+ obj.emitRate = Math.round(Math.random() * 32) + 32
obj.go();
}
}
diff --git a/examples/declarative/particles/custom/fireworks.qml b/examples/declarative/particles/trails/fireworks.qml
index b73a5e234f..59627f8dce 100644
--- a/examples/declarative/particles/custom/fireworks.qml
+++ b/examples/declarative/particles/trails/fireworks.qml
@@ -45,58 +45,64 @@ Rectangle{
width: 360
height: 600
color: "black"
- Component{
- id: firework
- Item{
- id: container
- width: 48
- height: 48
- Image{
- width: 48
- height: 48
- id: img
- source: "content/particle.png"
- }
- Timer{
- interval: 1000 + 4000*Math.random()
- running: true
- repeat: false
- onTriggered: {
- img.visible = false;
- emitter.burst(100);
- }
- }
- Emitter{
- anchors.centerIn: parent
- id: emitter
- system: syssy
- particle: "works"
- emitting: false
- emitRate: 100
- lifeSpan: 1000
- //speed: AngledDirection{angle: 270; angleVariation:60; magnitudeVariation: 60; magnitude: 20}
- speed: PointDirection{y:-60; yVariation: 80; xVariation: 80}
- acceleration: PointDirection{y:100; yVariation: 20}
- }
- }
- }
ParticleSystem{
anchors.fill: parent
id: syssy
+ particleStates:[
+ Sprite{
+ name: "fire"
+ duration: 2000
+ to: {"splode":1}
+ },
+ Sprite{
+ name: "splode"
+ duration: 400
+ to: {"dead":1}
+ FollowEmitter{
+ particle: "works"
+ emitRatePerParticle: 100
+ lifeSpan: 1000
+ emitCap: 1200
+ size: 8
+ speed: AngledDirection{angle: 270; angleVariation: 45; magnitude: 20; magnitudeVariation: 20;}
+ acceleration: PointDirection{y:100; yVariation: 20}
+ }
+ },
+ Sprite{
+ name: "dead"
+ duration: 1000
+ Affector{
+ onceOff: true
+ signal: true
+ onAffected: worksEmitter.burst(400,x,y)
+ }
+ }
+ ]
Emitter{
particle: "fire"
width: parent.width
y: parent.height
emitRate: 2
lifeSpan: 6000
- speed: PointDirection{y:-100}
+ speed: PointDirection{y:-100; yVariation: 40}
+ size: 32
}
- ItemParticle{
- particles: ["fire"]
- delegate: firework
+ Emitter{
+ id: worksEmitter
+ particle: "works"
+ emitting: false
+ emitRate: 100
+ lifeSpan: 1600
+ emitCap: 6400
+ size: 8
+ speed: CumulativeDirection{
+ PointDirection{y:-100}
+ AngledDirection{angleVariation: 360; magnitudeVariation: 80;}
+ }
+ acceleration: PointDirection{y:100; yVariation: 20}
}
ImageParticle{
- particles: ["works"]
+ particles: ["works", "fire", "splode"]
source: "content/particle.png"
}
}
diff --git a/src/declarative/items/qsgsprite.cpp b/src/declarative/items/qsgsprite.cpp
index 694976a540..794ae519a3 100644
--- a/src/declarative/items/qsgsprite.cpp
+++ b/src/declarative/items/qsgsprite.cpp
@@ -40,6 +40,9 @@
****************************************************************************/
#include "qsgsprite_p.h"
+//TODO: Split out particle system dependency
+#include "qsgparticlesystem_p.h"
+#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -54,4 +57,17 @@ QSGSprite::QSGSprite(QObject *parent) :
{
}
+void redirectError(QDeclarativeListProperty<QObject> *prop, QObject *value)
+{
+ qWarning() << "Could not add " << value << " to state" << prop->object << "as it is not associated with a particle system.";
+}
+
+QDeclarativeListProperty<QObject> QSGSprite::particleChildren(){
+ QSGParticleSystem* system = qobject_cast<QSGParticleSystem*>(parent());
+ if (system)
+ return QDeclarativeListProperty<QObject>(this, 0, &QSGParticleSystem::stateRedirect);
+ else
+ return QDeclarativeListProperty<QObject>(this, 0, &redirectError);
+}
+
QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgsprite_p.h b/src/declarative/items/qsgsprite_p.h
index 652a4cd482..c20599853f 100644
--- a/src/declarative/items/qsgsprite_p.h
+++ b/src/declarative/items/qsgsprite_p.h
@@ -45,6 +45,7 @@
#include <QObject>
#include <QUrl>
#include <QVariantMap>
+#include <QDeclarativeListProperty>
QT_BEGIN_HEADER
@@ -64,13 +65,17 @@ class QSGSprite : public QObject
Q_PROPERTY(int frameHeight READ frameHeight WRITE setFrameHeight NOTIFY frameHeightChanged)
Q_PROPERTY(int frameWidth READ frameWidth WRITE setFrameWidth NOTIFY frameWidthChanged)
Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
- Q_PROPERTY(int durationVariance READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged)
+ Q_PROPERTY(int durationVariation READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged)
Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged)
Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged)
+ Q_PROPERTY(QDeclarativeListProperty<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")
public:
explicit QSGSprite(QObject *parent = 0);
+ QDeclarativeListProperty<QObject> particleChildren();
+
QUrl source() const
{
return m_source;
@@ -136,6 +141,8 @@ signals:
void durationVarianceChanged(int arg);
+ void entered();//### Just playing around - don't expect full state API
+
public slots:
void setSource(QUrl arg)
@@ -224,6 +231,7 @@ private:
QVariantMap m_to;
qreal m_speedModifier;
int m_durationVariance;
+
};
QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgspriteengine.cpp b/src/declarative/items/qsgspriteengine.cpp
index 27de0d94f6..94753d09bc 100644
--- a/src/declarative/items/qsgspriteengine.cpp
+++ b/src/declarative/items/qsgspriteengine.cpp
@@ -48,6 +48,11 @@
QT_BEGIN_NAMESPACE
+/* TODO: Split out image logic from stochastic state logic
+ Also make sharable
+ Also solve the state data initialization/transfer issue so as to not need to make friends
+*/
+
QSGSpriteEngine::QSGSpriteEngine(QObject *parent) :
QObject(parent), m_timeOffset(0)
{
@@ -82,7 +87,7 @@ TODO: All these calculations should be pre-calculated and cached during initiali
int QSGSpriteEngine::spriteState(int sprite)
{
int state = m_sprites[sprite];
- if(!m_states[state]->m_generatedCount)
+ if (!m_states[state]->m_generatedCount)
return state;
int rowDuration = m_states[state]->duration() * m_states[state]->m_framesPerRow;
int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
@@ -92,7 +97,7 @@ int QSGSpriteEngine::spriteState(int sprite)
int QSGSpriteEngine::spriteStart(int sprite)
{
int state = m_sprites[sprite];
- if(!m_states[state]->m_generatedCount)
+ if (!m_states[state]->m_generatedCount)
return m_startTimes[sprite];
int rowDuration = m_states[state]->duration() * m_states[state]->m_framesPerRow;
int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
@@ -102,11 +107,11 @@ int QSGSpriteEngine::spriteStart(int sprite)
int QSGSpriteEngine::spriteFrames(int sprite)
{
int state = m_sprites[sprite];
- if(!m_states[state]->m_generatedCount)
+ if (!m_states[state]->m_generatedCount)
return m_states[state]->frames();
int rowDuration = m_states[state]->duration() * m_states[state]->m_framesPerRow;
int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
- if(extra == m_states[state]->m_generatedCount - 1)//last state
+ if (extra == m_states[state]->m_generatedCount - 1)//last state
return m_states[state]->frames() % m_states[state]->m_framesPerRow;
else
return m_states[state]->m_framesPerRow;
@@ -115,11 +120,11 @@ int QSGSpriteEngine::spriteFrames(int sprite)
int QSGSpriteEngine::spriteDuration(int sprite)
{
int state = m_sprites[sprite];
- if(!m_states[state]->m_generatedCount)
+ if (!m_states[state]->m_generatedCount)
return m_states[state]->duration();
int rowDuration = m_states[state]->duration() * m_states[state]->m_framesPerRow;
int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration;
- if(extra == m_states[state]->m_generatedCount - 1)//last state
+ if (extra == m_states[state]->m_generatedCount - 1)//last state
return (m_states[state]->duration() * m_states[state]->frames()) % rowDuration;
else
return rowDuration;
@@ -132,18 +137,20 @@ int QSGSpriteEngine::spriteCount()//TODO: Actually image state count, need to re
void QSGSpriteEngine::setGoal(int state, int sprite, bool jump)
{
- if(sprite >= m_sprites.count() || state >= m_states.count())
+ if (sprite >= m_sprites.count() || state >= m_states.count())
return;
- if(!jump){
+ if (!jump){
m_goals[sprite] = state;
return;
}
- if(m_sprites[sprite] == state)
+ if (m_sprites[sprite] == state)
return;//Already there
m_sprites[sprite] = state;
m_goals[sprite] = -1;
restartSprite(sprite);
+ emit stateChanged(sprite);
+ emit m_states[state]->entered();
return;
}
@@ -157,8 +164,8 @@ QImage QSGSpriteEngine::assembledImage()
int maxSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
- foreach(QSGSprite* state, m_states){
- if(state->frames() > m_maxFrames)
+ foreach (QSGSprite* state, m_states){
+ if (state->frames() > m_maxFrames)
m_maxFrames = state->frames();
QImage img(state->source().toLocalFile());
@@ -169,10 +176,10 @@ QImage QSGSpriteEngine::assembledImage()
//Check that the frame sizes are the same within one engine
int imgWidth = state->frameWidth();
- if(!imgWidth)
+ if (!imgWidth)
imgWidth = img.width() / state->frames();
- if(frameWidth){
- if(imgWidth != frameWidth){
+ if (frameWidth){
+ if (imgWidth != frameWidth){
qWarning() << "SpriteEngine: Irregular frame width..." << state->source().toLocalFile();
return QImage();
}
@@ -181,10 +188,10 @@ QImage QSGSpriteEngine::assembledImage()
}
int imgHeight = state->frameHeight();
- if(!imgHeight)
+ if (!imgHeight)
imgHeight = img.height();
- if(frameHeight){
- if(imgHeight!=frameHeight){
+ if (frameHeight){
+ if (imgHeight!=frameHeight){
qWarning() << "SpriteEngine: Irregular frame height..." << state->source().toLocalFile();
return QImage();
}
@@ -192,12 +199,12 @@ QImage QSGSpriteEngine::assembledImage()
frameHeight = imgHeight;
}
- if(state->frames() * frameWidth > maxSize){
+ if (state->frames() * frameWidth > maxSize){
struct helper{
static int divRoundUp(int a, int b){return (a+b-1)/b;}
};
int rowsNeeded = helper::divRoundUp(state->frames(), helper::divRoundUp(maxSize, frameWidth));
- if(rowsNeeded * frameHeight > maxSize){
+ if (rowsNeeded * frameHeight > maxSize){
qWarning() << "SpriteEngine: Animation too large to fit in one texture..." << state->source().toLocalFile();
qWarning() << "SpriteEngine: Your texture max size today is " << maxSize;
}
@@ -209,15 +216,15 @@ QImage QSGSpriteEngine::assembledImage()
}
//maxFrames is max number in a line of the texture
- if(m_maxFrames * frameWidth > maxSize)
+ if (m_maxFrames * frameWidth > maxSize)
m_maxFrames = maxSize/frameWidth;
QImage image(frameWidth * m_maxFrames, frameHeight * m_imageStateCount, QImage::Format_ARGB32);
image.fill(0);
QPainter p(&image);
int y = 0;
- foreach(QSGSprite* state, m_states){
+ foreach (QSGSprite* state, m_states){
QImage img(state->source().toLocalFile());
- if(img.height() == frameHeight && img.width() < maxSize){//Simple case
+ if (img.height() == frameHeight && img.width() < maxSize){//Simple case
p.drawImage(0,y,img);
y += frameHeight;
}else{
@@ -226,8 +233,8 @@ QImage QSGSpriteEngine::assembledImage()
int curX = 0;
int curY = 0;
int framesLeft = state->frames();
- while(framesLeft > 0){
- if(image.width() - x + curX <= img.width()){//finish a row in image (dest)
+ while (framesLeft > 0){
+ if (image.width() - x + curX <= img.width()){//finish a row in image (dest)
int copied = image.width() - x;
Q_ASSERT(!(copied % frameWidth));//XXX: Just checking
framesLeft -= copied/frameWidth;
@@ -235,7 +242,7 @@ QImage QSGSpriteEngine::assembledImage()
y += frameHeight;
curX += copied;
x = 0;
- if(curX == img.width()){
+ if (curX == img.width()){
curX = 0;
curY += frameHeight;
}
@@ -249,12 +256,12 @@ QImage QSGSpriteEngine::assembledImage()
curX = 0;
}
}
- if(x)
+ if (x)
y += frameHeight;
}
}
- if(image.height() > maxSize){
+ if (image.height() > maxSize){
qWarning() << "SpriteEngine: Too many animations to fit in one texture...";
qWarning() << "SpriteEngine: Your texture max size today is " << maxSize;
return QImage();
@@ -269,53 +276,63 @@ void QSGSpriteEngine::setCount(int c)
m_startTimes.resize(c);
}
-void QSGSpriteEngine::startSprite(int index)
+void QSGSpriteEngine::startSprite(int index, int state)
{
- if(index >= m_sprites.count())
+ if (index >= m_sprites.count())
return;
- m_sprites[index] = 0;
+ m_sprites[index] = state;
m_goals[index] = -1;
restartSprite(index);
}
+void QSGSpriteEngine::stopSprite(int index)
+{
+ if (index >= m_sprites.count())
+ return;
+ //Will never change until start is called again with a new state - this is not a 'pause'
+ for (int i=0; i<m_stateUpdates.count(); i++)
+ m_stateUpdates[i].second.removeAll(index);
+}
+
void QSGSpriteEngine::restartSprite(int index)
{
m_startTimes[index] = m_timeOffset + m_advanceTime.elapsed();
int time = m_states[m_sprites[index]]->duration() * m_states[m_sprites[index]]->frames() + m_startTimes[index];
- for(int i=0; i<m_stateUpdates.count(); i++)
+ for (int i=0; i<m_stateUpdates.count(); i++)
m_stateUpdates[i].second.removeAll(index);
addToUpdateList(time, index);
}
-uint QSGSpriteEngine::updateSprites(uint time)
+uint QSGSpriteEngine::updateSprites(uint time)//### would returning a list of changed idxs be faster than signals?
{
//Sprite State Update;
- while(!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){
- foreach(int idx, m_stateUpdates.first().second){
- if(idx >= m_sprites.count())
+ QSet<int> changedIndexes;
+ while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){
+ foreach (int idx, m_stateUpdates.first().second){
+ if (idx >= m_sprites.count())
continue;//TODO: Proper fix(because this does happen and I'm just ignoring it)
int stateIdx = m_sprites[idx];
int nextIdx = -1;
int goalPath = goalSeek(stateIdx, idx);
- if(goalPath == -1){//Random
+ if (goalPath == -1){//Random
qreal r =(qreal) qrand() / (qreal) RAND_MAX;
qreal total = 0.0;
- for(QVariantMap::const_iterator iter=m_states[stateIdx]->m_to.constBegin();
+ for (QVariantMap::const_iterator iter=m_states[stateIdx]->m_to.constBegin();
iter!=m_states[stateIdx]->m_to.constEnd(); iter++)
total += (*iter).toReal();
r*=total;
- for(QVariantMap::const_iterator iter= m_states[stateIdx]->m_to.constBegin();
+ for (QVariantMap::const_iterator iter= m_states[stateIdx]->m_to.constBegin();
iter!=m_states[stateIdx]->m_to.constEnd(); iter++){
- if(r < (*iter).toReal()){
+ if (r < (*iter).toReal()){
bool superBreak = false;
- for(int i=0; i<m_states.count(); i++){
- if(m_states[i]->name() == iter.key()){
+ for (int i=0; i<m_states.count(); i++){
+ if (m_states[i]->name() == iter.key()){
nextIdx = i;
superBreak = true;
break;
}
}
- if(superBreak)
+ if (superBreak)
break;
}
r -= (*iter).toReal();
@@ -323,12 +340,15 @@ uint QSGSpriteEngine::updateSprites(uint time)
}else{//Random out of shortest paths to goal
nextIdx = goalPath;
}
- if(nextIdx == -1)//No to states means stay here
+ if (nextIdx == -1)//No to states means stay here
nextIdx = stateIdx;
m_sprites[idx] = nextIdx;
m_startTimes[idx] = time;
- //TODO: emit something? Remember to emit this when a psuedostate changes too
+ if (nextIdx != stateIdx){
+ changedIndexes << idx;
+ emit m_states[nextIdx]->entered();
+ }
addToUpdateList((m_states[nextIdx]->duration() * m_states[nextIdx]->frames()) + time, idx);
}
m_stateUpdates.pop_front();
@@ -336,7 +356,11 @@ uint QSGSpriteEngine::updateSprites(uint time)
m_timeOffset = time;
m_advanceTime.start();
- if(m_stateUpdates.isEmpty())
+ //TODO: emit this when a psuedostate changes too
+ foreach (int idx, changedIndexes){//Batched so that update list doesn't change midway
+ emit stateChanged(idx);
+ }
+ if (m_stateUpdates.isEmpty())
return -1;
return m_stateUpdates.first().first;
}
@@ -344,68 +368,68 @@ uint QSGSpriteEngine::updateSprites(uint time)
int QSGSpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist)
{
QString goalName;
- if(m_goals[spriteIdx] != -1)
+ if (m_goals[spriteIdx] != -1)
goalName = m_states[m_goals[spriteIdx]]->name();
else
goalName = m_globalGoal;
- if(goalName.isEmpty())
+ if (goalName.isEmpty())
return -1;
//TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitarily anyways)
// Paraphrased - implement in an *efficient* manner
- for(int i=0; i<m_states.count(); i++)
- if(m_states[curIdx]->name() == goalName)
+ for (int i=0; i<m_states.count(); i++)
+ if (m_states[curIdx]->name() == goalName)
return curIdx;
- if(dist < 0)
+ if (dist < 0)
dist = m_states.count();
QSGSprite* curState = m_states[curIdx];
- for(QVariantMap::const_iterator iter = curState->m_to.constBegin();
+ for (QVariantMap::const_iterator iter = curState->m_to.constBegin();
iter!=curState->m_to.constEnd(); iter++){
- if(iter.key() == goalName)
- for(int i=0; i<m_states.count(); i++)
- if(m_states[i]->name() == goalName)
+ if (iter.key() == goalName)
+ for (int i=0; i<m_states.count(); i++)
+ if (m_states[i]->name() == goalName)
return i;
}
QSet<int> options;
- for(int i=1; i<dist; i++){
- for(QVariantMap::const_iterator iter = curState->m_to.constBegin();
+ for (int i=1; i<dist; i++){
+ for (QVariantMap::const_iterator iter = curState->m_to.constBegin();
iter!=curState->m_to.constEnd(); iter++){
int option = -1;
- for(int j=0; j<m_states.count(); j++)//One place that could be a lot more efficient...
- if(m_states[j]->name() == iter.key())
- if(goalSeek(j, spriteIdx, i) != -1)
+ for (int j=0; j<m_states.count(); j++)//One place that could be a lot more efficient...
+ if (m_states[j]->name() == iter.key())
+ if (goalSeek(j, spriteIdx, i) != -1)
option = j;
- if(option != -1)
+ if (option != -1)
options << option;
}
- if(!options.isEmpty()){
- if(options.count()==1)
+ if (!options.isEmpty()){
+ if (options.count()==1)
return *(options.begin());
int option = -1;
qreal r =(qreal) qrand() / (qreal) RAND_MAX;
qreal total;
- for(QSet<int>::const_iterator iter=options.constBegin();
+ for (QSet<int>::const_iterator iter=options.constBegin();
iter!=options.constEnd(); iter++)
total += curState->m_to.value(m_states[(*iter)]->name()).toReal();
r *= total;
- for(QVariantMap::const_iterator iter = curState->m_to.constBegin();
+ for (QVariantMap::const_iterator iter = curState->m_to.constBegin();
iter!=curState->m_to.constEnd(); iter++){
bool superContinue = true;
- for(int j=0; j<m_states.count(); j++)
- if(m_states[j]->name() == iter.key())
- if(options.contains(j))
+ for (int j=0; j<m_states.count(); j++)
+ if (m_states[j]->name() == iter.key())
+ if (options.contains(j))
superContinue = false;
- if(superContinue)
+ if (superContinue)
continue;
- if(r < (*iter).toReal()){
+ if (r < (*iter).toReal()){
bool superBreak = false;
- for(int j=0; j<m_states.count(); j++){
- if(m_states[j]->name() == iter.key()){
+ for (int j=0; j<m_states.count(); j++){
+ if (m_states[j]->name() == iter.key()){
option = j;
superBreak = true;
break;
}
}
- if(superBreak)
+ if (superBreak)
break;
}
r-=(*iter).toReal();
@@ -418,11 +442,11 @@ int QSGSpriteEngine::goalSeek(int curIdx, int spriteIdx, int dist)
void QSGSpriteEngine::addToUpdateList(uint t, int idx)
{
- for(int i=0; i<m_stateUpdates.count(); i++){
- if(m_stateUpdates[i].first==t){
+ for (int i=0; i<m_stateUpdates.count(); i++){
+ if (m_stateUpdates[i].first==t){
m_stateUpdates[i].second << idx;
return;
- }else if(m_stateUpdates[i].first > t){
+ }else if (m_stateUpdates[i].first > t){
QList<int> tmpList;
tmpList << idx;
m_stateUpdates.insert(i, qMakePair(t, tmpList));
diff --git a/src/declarative/items/qsgspriteengine_p.h b/src/declarative/items/qsgspriteengine_p.h
index 8ab6e3a30a..33d6e82bd4 100644
--- a/src/declarative/items/qsgspriteengine_p.h
+++ b/src/declarative/items/qsgspriteengine_p.h
@@ -92,7 +92,8 @@ public:
void setGoal(int state, int sprite=0, bool jump=false);
QImage assembledImage();
- void startSprite(int index=0);
+ void startSprite(int index=0, int state=0);
+ void stopSprite(int index=0);
private://Nothing outside should use this?
friend class QSGSpriteGoalAffector;//XXX: Fix interface
@@ -102,6 +103,7 @@ private://Nothing outside should use this?
signals:
void globalGoalChanged(QString arg);
+ void stateChanged(int idx);
public slots:
void setGlobalGoal(QString arg)
@@ -115,6 +117,7 @@ public slots:
uint updateSprites(uint time);
private:
+ friend class QSGParticleSystem;
void restartSprite(int sprite);
void addToUpdateList(uint t, int idx);
int goalSeek(int curState, int spriteIdx, int dist=-1);
@@ -122,7 +125,7 @@ private:
QVector<int> m_sprites;//int is the index in m_states of the current state
QVector<int> m_goals;
QVector<int> m_startTimes;
- QList<QPair<uint, QList<int> > > m_stateUpdates;//### This could be done faster
+ QList<QPair<uint, QList<int> > > m_stateUpdates;//### This could be done faster - priority queue?
QTime m_advanceTime;
uint m_timeOffset;
diff --git a/src/declarative/particles/particles.pri b/src/declarative/particles/particles.pri
index 04200a380e..bdd42aea70 100644
--- a/src/declarative/particles/particles.pri
+++ b/src/declarative/particles/particles.pri
@@ -26,7 +26,9 @@ HEADERS += \
$$PWD/qsgstochasticdirection_p.h \
$$PWD/qsgtargeteddirection_p.h \
$$PWD/qsgturbulence_p.h \
- $$PWD/qsgwander_p.h
+ $$PWD/qsgwander_p.h \
+ $$PWD/qsgtargetaffector_p.h \
+ $$PWD/qsgcumulativedirection_p.h
SOURCES += \
$$PWD/qsgangleddirection.cpp \
@@ -54,7 +56,12 @@ SOURCES += \
$$PWD/qsgstochasticdirection.cpp \
$$PWD/qsgtargeteddirection.cpp \
$$PWD/qsgturbulence.cpp \
- $$PWD/qsgwander.cpp
+ $$PWD/qsgwander.cpp \
+ $$PWD/qsgtargetaffector.cpp \
+ $$PWD/qsgcumulativedirection.cpp
RESOURCES += \
$$PWD/particles.qrc
+
+
+
diff --git a/src/declarative/particles/qsgcumulativedirection.cpp b/src/declarative/particles/qsgcumulativedirection.cpp
new file mode 100644
index 0000000000..c6834aa8b1
--- /dev/null
+++ b/src/declarative/particles/qsgcumulativedirection.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgcumulativedirection_p.h"
+QT_BEGIN_NAMESPACE
+
+QSGCumulativeDirection::QSGCumulativeDirection(QObject *parent):QSGStochasticDirection(parent)
+{
+}
+
+QDeclarativeListProperty<QSGStochasticDirection> QSGCumulativeDirection::directions()
+{
+ return QDeclarativeListProperty<QSGStochasticDirection>(this, m_directions);//TODO: Proper list property
+}
+
+const QPointF &QSGCumulativeDirection::sample(const QPointF &from)
+{
+ QPointF ret;
+ foreach (QSGStochasticDirection* dir, m_directions)
+ ret += dir->sample(from);
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgcumulativedirection_p.h b/src/declarative/particles/qsgcumulativedirection_p.h
new file mode 100644
index 0000000000..aad003a7ce
--- /dev/null
+++ b/src/declarative/particles/qsgcumulativedirection_p.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCUMULATIVEDIRECTION_P_H
+#define QSGCUMULATIVEDIRECTION_P_H
+#include "qsgstochasticdirection_p.h"
+#include <QDeclarativeListProperty>
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGCumulativeDirection : public QSGStochasticDirection
+{
+ Q_OBJECT
+ Q_PROPERTY(QDeclarativeListProperty<QSGStochasticDirection> directions READ directions)
+ Q_CLASSINFO("DefaultProperty", "directions")
+public:
+ explicit QSGCumulativeDirection(QObject *parent = 0);
+ QDeclarativeListProperty<QSGStochasticDirection> directions();
+ const QPointF &sample(const QPointF &from);
+private:
+ QList<QSGStochasticDirection*> m_directions;
+};
+#endif // QSGCUMULATIVEDIRECTION_P_H
diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp
index 46f160ddf4..ba6bf2951c 100644
--- a/src/declarative/particles/qsgcustomparticle.cpp
+++ b/src/declarative/particles/qsgcustomparticle.cpp
@@ -123,6 +123,7 @@ QSGCustomParticle::QSGCustomParticle(QSGItem* parent)
: QSGParticlePainter(parent)
, m_pleaseReset(true)
, m_dirtyData(true)
+ , m_rootNode(0)
{
setFlag(QSGItem::ItemHasContents);
}
@@ -194,6 +195,7 @@ void QSGCustomParticle::reset()
QSGParticlePainter::reset();
m_pleaseReset = true;
+ update();
}
@@ -334,7 +336,7 @@ void QSGCustomParticle::lookThroughShaderCode(const QByteArray &code)
QByteArray name = re.cap(3).toLatin1(); // variable name
if (decl == "attribute") {
- if(!m_source.attributeNames.contains(name))//TODO: Can they add custom attributes?
+ if (!m_source.attributeNames.contains(name))//TODO: Can they add custom attributes?
qWarning() << "Custom Particle: Unknown attribute " << name;
} else {
Q_ASSERT(decl == "uniform");//TODO: Shouldn't assert
@@ -361,90 +363,53 @@ void QSGCustomParticle::lookThroughShaderCode(const QByteArray &code)
QSGNode *QSGCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
- if(m_pleaseReset){
- if(m_node)
- delete m_node;
+ if (m_pleaseReset){
+
//delete m_material;//Shader effect item doesn't regen material?
- m_node = 0;
+ delete m_rootNode;//Automatically deletes children
+ m_rootNode = 0;
+ m_nodes.clear();
m_pleaseReset = false;
m_dirtyData = false;
}
- if(m_system && m_system->isRunning())
+ if (m_system && m_system->isRunning())
prepareNextFrame();
- if (m_node){
- if(oldNode)
- Q_ASSERT(oldNode == m_node);
+ if (m_rootNode){
update();
- m_node->markDirty(QSGNode::DirtyMaterial); //done in buildData?
+ //### Should I be using dirty geometry too/instead?
+ foreach (QSGShaderEffectNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial); //done in buildData?
}
- return m_node;
+ return m_rootNode;
}
void QSGCustomParticle::prepareNextFrame(){
- if(!m_node)
- m_node = buildCustomNode();
- if(!m_node)
+ if (!m_rootNode)
+ m_rootNode = buildCustomNodes();
+ if (!m_rootNode)
return;
m_lastTime = m_system->systemSync(this) / 1000.;
- if(m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive.
+ if (m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive.
buildData();
}
-QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
+QSGShaderEffectNode* QSGCustomParticle::buildCustomNodes()
{
if (m_count * 4 > 0xffff) {
printf("CustomParticle: Too many particles... \n");//####Why is this here?
return 0;
}
- if(m_count <= 0) {
+ if (m_count <= 0) {
printf("CustomParticle: Too few particles... \n");
return 0;
}
- //Create Particle Geometry
- int vCount = m_count * 4;
- int iCount = m_count * 6;
- QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount);
- g->setDrawingMode(GL_TRIANGLES);
- PlainVertex *vertices = (PlainVertex *) g->vertexData();
- for (int p=0; p<m_count; ++p) {
- reload(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<m_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;
- }
-
updateProperties();
- QSGShaderEffectNode* node = new QSGShaderEffectNode();
-
-
- node->setGeometry(g);
- node->setMaterial(&m_material);
QSGShaderEffectProgram s = m_source;
if (s.fragmentCode.isEmpty())
@@ -452,66 +417,120 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
if (s.vertexCode.isEmpty())
s.vertexCode = qt_particles_default_vertex_code;
m_material.setProgramSource(s);
- return node;
+ foreach (const QString &str, m_particles){
+ int gIdx = m_system->m_groupIds[str];
+ int count = m_system->m_groupData[gIdx]->size();
+ //Create Particle Geometry
+ int vCount = count * 4;
+ int iCount = count * 6;
+ QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount);
+ g->setDrawingMode(GL_TRIANGLES);
+ 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;
+ }
+
+ QSGShaderEffectNode* node = new QSGShaderEffectNode();
+
+ node->setGeometry(g);
+ node->setMaterial(&m_material);
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ m_nodes.insert(gIdx, node);
+ }
+ foreach (QSGShaderEffectNode* node, m_nodes){
+ if (node == *(m_nodes.begin()))
+ continue;
+ (*(m_nodes.begin()))->appendChildNode(node);
+ }
+
+ return *(m_nodes.begin());
}
static const QByteArray timestampName("timestamp");
void QSGCustomParticle::buildData()
{
- if(!m_node)//Operates on m_node
+ if (!m_rootNode)
return;
QVector<QPair<QByteArray, QVariant> > values;
QVector<QPair<QByteArray, QPointer<QSGItem> > > textures;
const QVector<QPair<QByteArray, QPointer<QSGItem> > > &oldTextures = m_material.textureProviders();
-
- for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
- it != m_source.uniformNames.end(); ++it) {
- values.append(qMakePair(*it, property(*it)));
- }
for (int i = 0; i < oldTextures.size(); ++i) {
QSGTextureProvider *oldSource = QSGTextureProvider::from(oldTextures.at(i).second);
if (oldSource && oldSource->textureChangedSignal())
- disconnect(oldTextures.at(i).second, oldSource->textureChangedSignal(), m_node, SLOT(markDirtyTexture()));
+ foreach (QSGShaderEffectNode* node, m_nodes)
+ disconnect(oldTextures.at(i).second, oldSource->textureChangedSignal(), node, SLOT(markDirtyTexture()));
}
for (int i = 0; i < m_sources.size(); ++i) {
const SourceData &source = m_sources.at(i);
textures.append(qMakePair(source.name, source.item));
QSGTextureProvider *t = QSGTextureProvider::from(source.item);
if (t && t->textureChangedSignal())
- connect(source.item, t->textureChangedSignal(), m_node, SLOT(markDirtyTexture()), Qt::DirectConnection);
+ foreach (QSGShaderEffectNode* node, m_nodes)
+ connect(source.item, t->textureChangedSignal(), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
+ }
+ for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
+ it != m_source.uniformNames.end(); ++it) {
+ values.append(qMakePair(*it, property(*it)));
}
values.append(qMakePair(timestampName, QVariant(m_lastTime)));
m_material.setUniforms(values);
m_material.setTextureProviders(textures);
- m_node->markDirty(QSGNode::DirtyMaterial);
m_dirtyData = false;
+ foreach (QSGShaderEffectNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
}
-void QSGCustomParticle::initialize(int idx)
+void QSGCustomParticle::initialize(int gIdx, int pIdx)
{
- m_data[idx]->r = rand()/(qreal)RAND_MAX;
+ QSGParticleData* datum = m_system->m_groupData[gIdx]->data[pIdx];
+ datum->r = rand()/(qreal)RAND_MAX;
}
-void QSGCustomParticle::reload(int idx)
+void QSGCustomParticle::commit(int gIdx, int pIdx)
{
- if (m_node == 0)
+ if (m_nodes[gIdx] == 0)
return;
- PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData();
- PlainVertex *vertices = (PlainVertex *)&particles[idx];
+ QSGParticleData* datum = m_system->m_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 = m_data[idx]->x - m_systemOffset.x();
- vertices[i].y = m_data[idx]->y - m_systemOffset.y();
- vertices[i].t = m_data[idx]->t;
- vertices[i].lifeSpan = m_data[idx]->lifeSpan;
- vertices[i].size = m_data[idx]->size;
- vertices[i].endSize = m_data[idx]->endSize;
- vertices[i].sx = m_data[idx]->sx;
- vertices[i].sy = m_data[idx]->sy;
- vertices[i].ax = m_data[idx]->ax;
- vertices[i].ay = m_data[idx]->ay;
- vertices[i].r = m_data[idx]->r;
+ 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].sx = datum->sx;
+ vertices[i].sy = datum->sy;
+ vertices[i].ax = datum->ax;
+ vertices[i].ay = datum->ay;
+ vertices[i].r = datum->r;
}
}
diff --git a/src/declarative/particles/qsgcustomparticle_p.h b/src/declarative/particles/qsgcustomparticle_p.h
index 50ff37a2a0..cccaa7e3a8 100644
--- a/src/declarative/particles/qsgcustomparticle_p.h
+++ b/src/declarative/particles/qsgcustomparticle_p.h
@@ -75,8 +75,8 @@ Q_SIGNALS:
void fragmentShaderChanged();
void vertexShaderChanged();
protected:
- virtual void initialize(int idx);
- virtual void reload(int idx);
+ virtual void initialize(int gIdx, int pIdx);
+ virtual void commit(int gIdx, int pIdx);
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
void prepareNextFrame();
@@ -88,7 +88,7 @@ protected:
void updateProperties();
void lookThroughShaderCode(const QByteArray &code);
virtual void componentComplete();
- QSGShaderEffectNode *buildCustomNode();
+ QSGShaderEffectNode *buildCustomNodes();
void performPendingResize();
private:
@@ -106,7 +106,8 @@ private:
};
QVector<SourceData> m_sources;
QSGShaderEffectMaterial m_material;
- QSGShaderEffectNode* m_node;
+ QSGShaderEffectNode* m_rootNode;
+ QHash<int, QSGShaderEffectNode*> m_nodes;
qreal m_lastTime;
};
diff --git a/src/declarative/particles/qsgemitter.cpp b/src/declarative/particles/qsgemitter.cpp
index c3ec3ffc9e..8204f4d162 100644
--- a/src/declarative/particles/qsgemitter.cpp
+++ b/src/declarative/particles/qsgemitter.cpp
@@ -41,7 +41,6 @@
#include "qsgemitter_p.h"
#include "qsgparticlesystem_p.h"
-#include "qsgparticlepainter_p.h"//TODO: What was this for again?
QT_BEGIN_NAMESPACE
QSGBasicEmitter::QSGBasicEmitter(QSGItem* parent)
@@ -72,7 +71,7 @@ void QSGBasicEmitter::emitWindow(int timeStamp)
{
if (m_system == 0)
return;
- if((!m_emitting || !m_particlesPerSecond)&& !m_burstLeft && m_burstQueue.isEmpty()){
+ if ((!m_emitting || !m_particlesPerSecond)&& !m_burstLeft && m_burstQueue.isEmpty()){
m_reset_last = true;
return;
}
@@ -84,10 +83,10 @@ void QSGBasicEmitter::emitWindow(int timeStamp)
m_reset_last = false;
}
- if(m_burstLeft){
+ if (m_burstLeft){
m_burstLeft -= timeStamp - m_last_timestamp * 1000.;
- if(m_burstLeft < 0){
- if(!m_emitting)
+ if (m_burstLeft < 0){
+ if (!m_emitting)
timeStamp += m_burstLeft;
m_burstLeft = 0;
}
@@ -100,7 +99,7 @@ void QSGBasicEmitter::emitWindow(int timeStamp)
qreal opt = pt; // original particle time
qreal dt = time - m_last_timestamp; // timestamp delta...
- if(!dt)
+ if (!dt)
dt = 0.000001;
// emitter difference since last...
@@ -117,12 +116,12 @@ void QSGBasicEmitter::emitWindow(int timeStamp)
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_burstLeft && !m_emitting)//'outside time' emissions only
+ if (!m_burstQueue.isEmpty() && !m_burstLeft && !m_emitting)//'outside time' emissions only
pt = time;
while (pt < time || !m_burstQueue.isEmpty()) {
//int pos = m_last_particle % m_particle_count;
QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle]);
- if(datum){//actually emit(otherwise we've been asked to skip this one)
+ 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 =
@@ -144,7 +143,7 @@ void QSGBasicEmitter::emitWindow(int timeStamp)
// Particle position
QRectF boundsRect;
- if(!m_burstQueue.isEmpty()){
+ if (!m_burstQueue.isEmpty()){
boundsRect = QRectF(m_burstQueue.first().second.x() - x(), m_burstQueue.first().second.y() - y(),
width(), height());
} else {
@@ -179,11 +178,11 @@ void QSGBasicEmitter::emitWindow(int timeStamp)
m_system->emitParticle(datum);
}
- if(m_burstQueue.isEmpty()){
+ if (m_burstQueue.isEmpty()){
pt += particleRatio;
}else{
m_burstQueue.first().first--;
- if(m_burstQueue.first().first <= 0)
+ if (m_burstQueue.first().first <= 0)
m_burstQueue.pop_front();
}
}
diff --git a/src/declarative/particles/qsgfollowemitter.cpp b/src/declarative/particles/qsgfollowemitter.cpp
index ba31e8f834..72577fd816 100644
--- a/src/declarative/particles/qsgfollowemitter.cpp
+++ b/src/declarative/particles/qsgfollowemitter.cpp
@@ -64,15 +64,15 @@ QSGFollowEmitter::QSGFollowEmitter(QSGItem *parent) :
}
void QSGFollowEmitter::recalcParticlesPerSecond(){
- if(!m_system)
+ if (!m_system)
return;
- m_followCount = m_system->m_groupData[m_system->m_groupIds[m_follow]]->size;
- if(!m_followCount){
+ m_followCount = m_system->m_groupData[m_system->m_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(0);
+ m_lastEmission.fill(m_lastTimeStamp);
}
}
@@ -85,18 +85,18 @@ void QSGFollowEmitter::emitWindow(int timeStamp)
{
if (m_system == 0)
return;
- if(!m_emitting && !m_burstLeft && m_burstQueue.isEmpty())
+ if (!m_emitting && !m_burstLeft && m_burstQueue.isEmpty())
return;
- if(m_followCount != m_system->m_groupData[m_system->m_groupIds[m_follow]]->size){
+ if (m_followCount != m_system->m_groupData[m_system->m_groupIds[m_follow]]->size()){
qreal oldPPS = m_particlesPerSecond;
recalcParticlesPerSecond();
- if(m_particlesPerSecond != oldPPS)
+ if (m_particlesPerSecond != oldPPS)
return;//system may need to update
}
- if(m_burstLeft){
+ if (m_burstLeft){
m_burstLeft -= timeStamp - m_lastTimeStamp * 1000.;
- if(m_burstLeft < 0){
+ if (m_burstLeft < 0){
timeStamp += m_burstLeft;
m_burstLeft = 0;
}
@@ -112,20 +112,22 @@ void QSGFollowEmitter::emitWindow(int timeStamp)
int gId = m_system->m_groupIds[m_follow];
int gId2 = m_system->m_groupIds[m_particle];
- foreach(QSGParticleData *d, m_system->m_groupData[gId]->data){
- if(!d || !d->stillAlive())
+ foreach (QSGParticleData *d, m_system->m_groupData[gId]->data){
+ if (!d || !d->stillAlive()){
+ m_lastEmission[d->index] = time; //Should only start emitting when it returns to life
continue;
+ }
pt = m_lastEmission[d->index];
- if(pt < d->t)
+ if (pt < d->t)
pt = d->t;
- if(!effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){
+ 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;
}
- while(pt < time || !m_burstQueue.isEmpty()){
+ while (pt < time || !m_burstQueue.isEmpty()){
QSGParticleData* datum = m_system->newDatum(gId2);
- if(datum){//else, skip this emission
+ if (datum){//else, skip this emission
datum->e = this;//###useful?
// Particle timestamp
@@ -178,9 +180,9 @@ void QSGFollowEmitter::emitWindow(int timeStamp)
m_system->emitParticle(datum);
}
- if(!m_burstQueue.isEmpty()){
+ if (!m_burstQueue.isEmpty()){
m_burstQueue.first().first--;
- if(m_burstQueue.first().first <= 0)
+ if (m_burstQueue.first().first <= 0)
m_burstQueue.pop_front();
}else{
pt += particleRatio;
diff --git a/src/declarative/particles/qsgfriction.cpp b/src/declarative/particles/qsgfriction.cpp
index 828d20556d..46816817fd 100644
--- a/src/declarative/particles/qsgfriction.cpp
+++ b/src/declarative/particles/qsgfriction.cpp
@@ -48,7 +48,7 @@ QSGFrictionAffector::QSGFrictionAffector(QSGItem *parent) :
bool QSGFrictionAffector::affectParticle(QSGParticleData *d, qreal dt)
{
- if(!m_factor)
+ if (!m_factor)
return false;
qreal curSX = d->curSX();
qreal curSY = d->curSY();
diff --git a/src/declarative/particles/qsggravity.cpp b/src/declarative/particles/qsggravity.cpp
index b1cf3e9481..579fa7c4e7 100644
--- a/src/declarative/particles/qsggravity.cpp
+++ b/src/declarative/particles/qsggravity.cpp
@@ -64,11 +64,11 @@ bool QSGGravityAffector::affectParticle(QSGParticleData *d, qreal dt)
{
Q_UNUSED(dt);
bool changed = false;
- if(d->ax != m_xAcc){
+ if (d->ax != m_xAcc){
d->setInstantaneousAX(m_xAcc);
changed = true;
}
- if(d->ay != m_yAcc){
+ if (d->ay != m_yAcc){
d->setInstantaneousAY(m_yAcc);
changed = true;
}
diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp
index 836236c13e..ac64d589f5 100644
--- a/src/declarative/particles/qsgimageparticle.cpp
+++ b/src/declarative/particles/qsgimageparticle.cpp
@@ -193,7 +193,7 @@ float UltraMaterialData::chunkOfBytes[1024];
QSGMaterialShader *UltraMaterial::createShader() const
{
- if(usesSprites)//TODO: Perhaps just swap the shaders, and don't mind the extra vector?
+ if (usesSprites)//TODO: Perhaps just swap the shaders, and don't mind the extra vector?
return new UltraMaterialData;
else
return new UltraMaterialData;
@@ -285,7 +285,7 @@ QSGImageParticle::QSGImageParticle(QSGItem* parent)
: QSGParticlePainter(parent)
, m_do_reset(false)
, m_color_variation(0.0)
- , m_node(0)
+ , m_rootNode(0)
, m_material(0)
, m_alphaVariation(0.0)
, m_alpha(1.0)
@@ -355,7 +355,7 @@ void QSGImageParticle::setColor(const QColor &color)
return;
m_color = color;
emit colorChanged();
- if(perfLevel < Colored)
+ if (perfLevel < Colored)
reset();
}
@@ -365,7 +365,7 @@ void QSGImageParticle::setColorVariation(qreal var)
return;
m_color_variation = var;
emit colorVariationChanged();
- if(perfLevel < Colored)
+ if (perfLevel < Colored)
reset();
}
@@ -375,7 +375,7 @@ void QSGImageParticle::setAlphaVariation(qreal arg)
m_alphaVariation = arg;
emit alphaVariationChanged(arg);
}
- if(perfLevel < Colored)
+ if (perfLevel < Colored)
reset();
}
@@ -385,7 +385,7 @@ void QSGImageParticle::setAlpha(qreal arg)
m_alpha = arg;
emit alphaChanged(arg);
}
- if(perfLevel < Colored)
+ if (perfLevel < Colored)
reset();
}
@@ -395,7 +395,7 @@ void QSGImageParticle::setRedVariation(qreal arg)
m_redVariation = arg;
emit redVariationChanged(arg);
}
- if(perfLevel < Colored)
+ if (perfLevel < Colored)
reset();
}
@@ -405,7 +405,7 @@ void QSGImageParticle::setGreenVariation(qreal arg)
m_greenVariation = arg;
emit greenVariationChanged(arg);
}
- if(perfLevel < Colored)
+ if (perfLevel < Colored)
reset();
}
@@ -415,7 +415,7 @@ void QSGImageParticle::setBlueVariation(qreal arg)
m_blueVariation = arg;
emit blueVariationChanged(arg);
}
- if(perfLevel < Colored)
+ if (perfLevel < Colored)
reset();
}
@@ -425,7 +425,7 @@ void QSGImageParticle::setRotation(qreal arg)
m_rotation = arg;
emit rotationChanged(arg);
}
- if(perfLevel < Deformable)
+ if (perfLevel < Deformable)
reset();
}
@@ -435,7 +435,7 @@ void QSGImageParticle::setRotationVariation(qreal arg)
m_rotationVariation = arg;
emit rotationVariationChanged(arg);
}
- if(perfLevel < Deformable)
+ if (perfLevel < Deformable)
reset();
}
@@ -445,7 +445,7 @@ void QSGImageParticle::setRotationSpeed(qreal arg)
m_rotationSpeed = arg;
emit rotationSpeedChanged(arg);
}
- if(perfLevel < Deformable)
+ if (perfLevel < Deformable)
reset();
}
@@ -455,7 +455,7 @@ void QSGImageParticle::setRotationSpeedVariation(qreal arg)
m_rotationSpeedVariation = arg;
emit rotationSpeedVariationChanged(arg);
}
- if(perfLevel < Deformable)
+ if (perfLevel < Deformable)
reset();
}
@@ -465,7 +465,7 @@ void QSGImageParticle::setAutoRotation(bool arg)
m_autoRotation = arg;
emit autoRotationChanged(arg);
}
- if(perfLevel < Deformable)
+ if (perfLevel < Deformable)
reset();
}
@@ -475,7 +475,7 @@ void QSGImageParticle::setXVector(QSGStochasticDirection* arg)
m_xVector = arg;
emit xVectorChanged(arg);
}
- if(perfLevel < Deformable)
+ if (perfLevel < Deformable)
reset();
}
@@ -485,7 +485,7 @@ void QSGImageParticle::setYVector(QSGStochasticDirection* arg)
m_yVector = arg;
emit yVectorChanged(arg);
}
- if(perfLevel < Deformable)
+ if (perfLevel < Deformable)
reset();
}
@@ -495,21 +495,22 @@ void QSGImageParticle::setBloat(bool arg)
m_bloat = arg;
emit bloatChanged(arg);
}
- if(perfLevel < 9999)
+ if (perfLevel < 9999)
reset();
}
void QSGImageParticle::reset()
{
QSGParticlePainter::reset();
- m_pleaseReset = true;
+ m_pleaseReset = true;
+ update();
}
void QSGImageParticle::createEngine()
{
- if(m_spriteEngine)
+ if (m_spriteEngine)
delete m_spriteEngine;
- if(m_sprites.count())
+ if (m_sprites.count())
m_spriteEngine = new QSGSpriteEngine(m_sprites, this);
else
m_spriteEngine = 0;
@@ -548,7 +549,7 @@ static QSGGeometry::AttributeSet UltraParticle_AttributeSet =
UltraParticle_Attributes
};
-QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode()
+QSGGeometryNode* QSGImageParticle::buildSimpleParticleNodes()
{
perfLevel = Simple;//TODO: Intermediate levels
QImage image = QImage(m_image_name.toLocalFile());
@@ -556,56 +557,6 @@ QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode()
printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile()));
return 0;
}
- int vCount = m_count * 4;
- int iCount = m_count * 6;
- qDebug() << "Simple Case";
-
- QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount);
- g->setDrawingMode(GL_TRIANGLES);
-
- SimpleVertex *vertices = (SimpleVertex *) g->vertexData();
- for (int p=0; p<m_count; ++p){
- for(int i=0; i<4; i++){
- vertices[i].x = m_data[p]->x;
- vertices[i].y = m_data[p]->y;
- vertices[i].t = m_data[p]->t;
- vertices[i].size = m_data[p]->size;
- vertices[i].endSize = m_data[p]->endSize;
- vertices[i].sx = m_data[p]->sx;
- vertices[i].sy = m_data[p]->sy;
- vertices[i].ax = m_data[p]->ax;
- vertices[i].ay = m_data[p]->ay;
- }
- //reload(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<m_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;
- }
-
- m_node = new QSGGeometryNode();
- m_node->setGeometry(g);
if (m_material) {
delete m_material;
@@ -616,25 +567,73 @@ QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode()
m_material->texture = sceneGraphEngine()->createTextureFromImage(image);
m_material->texture->setFiltering(QSGTexture::Linear);
m_material->framecount = 1;
- m_node->setMaterial(m_material);
- m_last_particle = 0;
- return m_node;
+ foreach (const QString &str, m_particles){
+ int gIdx = m_system->m_groupIds[str];
+ int count = m_system->m_groupData[gIdx]->size();
+
+ QSGGeometryNode* node = new QSGGeometryNode();
+ m_nodes.insert(gIdx, node);
+ node->setMaterial(m_material);
+
+ int vCount = count * 4;
+ int iCount = count * 6;
+
+ QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount);
+ node->setGeometry(g);
+ g->setDrawingMode(GL_TRIANGLES);
+
+ SimpleVertex *vertices = (SimpleVertex *) 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;
+ }
+ }
+
+ foreach (QSGGeometryNode* node, m_nodes){
+ if (node == *(m_nodes.begin()))
+ continue;
+ (*(m_nodes.begin()))->appendChildNode(node);
+ }
+
+ return *(m_nodes.begin());
}
-QSGGeometryNode* QSGImageParticle::buildParticleNode()
+QSGGeometryNode* QSGImageParticle::buildParticleNodes()
{
if (m_count * 4 > 0xffff) {
printf("UltraParticle: Too many particles... \n");//### Why is this here?
return 0;
}
- if(m_count <= 0) {
- qDebug() << "UltraParticle: Too few particles... \n";//XXX: Is now a vaild intermediate state...
+ if (count() <= 0)
return 0;
- }
- if(!m_sprites.count() && !m_bloat
+ if (!m_sprites.count() && !m_bloat
&& m_colortable_name.isEmpty()
&& m_sizetable_name.isEmpty()
&& m_opacitytable_name.isEmpty()
@@ -645,20 +644,19 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
&& !m_redVariation && !m_blueVariation && !m_greenVariation
&& !m_color.isValid()
)
- return buildSimpleParticleNode();
+ return buildSimpleParticleNodes();
perfLevel = Sprites;//TODO: intermediate levels
- if(!m_color.isValid())//But we're in colored level (or higher)
+ if (!m_color.isValid())//But we're in colored level (or higher)
m_color = QColor(Qt::white);
- qDebug() << "Complex Case";
QImage image;
- if(m_sprites.count()){
+ if (m_sprites.count()){
if (!m_spriteEngine) {
qWarning() << "UltraParticle: No sprite engine...";
return 0;
}
image = m_spriteEngine->assembledImage();
- if(image.isNull())//Warning is printed in engine
+ if (image.isNull())//Warning is printed in engine
return 0;
}else{
image = QImage(m_image_name.toLocalFile());
@@ -668,46 +666,6 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
}
}
- int vCount = m_count * 4;
- int iCount = m_count * 6;
-
- QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount);
- g->setDrawingMode(GL_TRIANGLES);
- m_node = new QSGGeometryNode();
- m_node->setGeometry(g);
-
- UltraVertex *vertices = (UltraVertex *) g->vertexData();
- for (int p=0; p<m_count; ++p) {
- reload(p);//reload gets geometry from node
-
- 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<m_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;
- }
-
- qFree(m_lastData);
if (m_material) {
delete m_material;
m_material = 0;
@@ -717,11 +675,11 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
QImage sizetable(m_sizetable_name.toLocalFile());
QImage opacitytable(m_opacitytable_name.toLocalFile());
m_material = new UltraMaterial();
- if(colortable.isNull())
+ if (colortable.isNull())
colortable = QImage(":defaultshaders/identitytable.png");
- if(sizetable.isNull())
+ if (sizetable.isNull())
sizetable = QImage(":defaultshaders/identitytable.png");
- if(opacitytable.isNull())
+ if (opacitytable.isNull())
opacitytable = QImage(":defaultshaders/defaultFadeInOut.png");
Q_ASSERT(!colortable.isNull());
Q_ASSERT(!sizetable.isNull());
@@ -734,55 +692,109 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
m_material->texture->setFiltering(QSGTexture::Linear);
m_material->framecount = 1;
- if(m_spriteEngine){
+ if (m_spriteEngine){
m_material->framecount = m_spriteEngine->maxFrames();
m_spriteEngine->setCount(m_count);
}
- m_node->setMaterial(m_material);
+ foreach (const QString &str, m_particles){
+ int gIdx = m_system->m_groupIds[str];
+ int count = m_system->m_groupData[gIdx]->size();
+ QSGGeometryNode* node = new QSGGeometryNode();
+ node->setMaterial(m_material);
+
+ m_nodes.insert(gIdx, node);
+ m_idxStarts.insert(gIdx, m_lastIdxStart);
+ m_lastIdxStart += count;
+
+ //Create Particle Geometry
+ int vCount = count * 4;
+ int iCount = count * 6;
+
+ QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount);
+ node->setGeometry(g);
+ g->setDrawingMode(GL_TRIANGLES);
+
+ UltraVertex *vertices = (UltraVertex *) g->vertexData();
+ for (int p=0; p < count; ++p) {
+ commit(gIdx, p);//commit sets geometry for the node
+
+ 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;
+ }
+
+ }
- m_last_particle = 0;
+ foreach (QSGGeometryNode* node, m_nodes){
+ if (node == *(m_nodes.begin()))
+ continue;
+ (*(m_nodes.begin()))->appendChildNode(node);
+ }
- return m_node;
+ return *(m_nodes.begin());
}
QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
{
- if(m_pleaseReset){
- if(m_node){
- if(perfLevel == 1){
- m_lastCount = m_node->geometry()->vertexCount() / 4;
- m_lastData = qMalloc(m_lastCount*sizeof(SimpleVertices));
- memcpy(m_lastData, m_node->geometry()->vertexData(), m_lastCount * sizeof(SimpleVertices));//TODO: Multiple levels
- }
- m_lastLevel = perfLevel;
- delete m_node;
- }
- if(m_material)
- delete m_material;
+ if (m_pleaseReset){
+ m_lastLevel = perfLevel;
+
+ delete m_rootNode;//Automatically deletes children
+ m_rootNode = 0;
+ m_nodes.clear();
- m_node = 0;
+ m_idxStarts.clear();
+ m_lastIdxStart = 0;
+
+ if (m_material)
+ delete m_material;
m_material = 0;
+
m_pleaseReset = false;
}
- if(m_system && m_system->isRunning())
+ if (m_system && m_system->isRunning())
prepareNextFrame();
- if (m_node){
+ if (m_rootNode){
update();
- m_node->markDirty(QSGNode::DirtyMaterial);
+ //### Should I be using dirty geometry too/instead?
+ foreach (QSGGeometryNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
}
- return m_node;
+ return m_rootNode;
}
void QSGImageParticle::prepareNextFrame()
{
- if (m_node == 0){//TODO: Staggered loading (as emitted)
- m_node = buildParticleNode();
- if(m_node == 0)
+ if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
+ m_rootNode = buildParticleNodes();
+ if (m_rootNode == 0)
return;
- qDebug() << "Feature level: " << perfLevel;
+ //qDebug() << "Feature level: " << perfLevel;
}
qint64 timeStamp = m_system->systemSync(this);
@@ -790,18 +802,25 @@ void QSGImageParticle::prepareNextFrame()
m_material->timestamp = time;
//Advance State
- if(m_spriteEngine){//perfLevel == Sprites?
+ if (m_spriteEngine){//perfLevel == Sprites?//TODO: use signals?
+
m_material->animcount = m_spriteEngine->spriteCount();
- UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
m_spriteEngine->updateSprites(timeStamp);
- for(int i=0; i<m_count; i++){
- UltraVertices &p = particles[i];
- int curIdx = m_spriteEngine->spriteState(i);
- if(curIdx != p.v1.animIdx){
- p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = curIdx;
- p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(i)/1000.0;
- p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(i);
- p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(i);
+ foreach (const QString &str, m_particles){
+ int gIdx = m_system->m_groupIds[str];
+ int count = m_system->m_groupData[gIdx]->size();
+
+ UltraVertices *particles = (UltraVertices *) m_nodes[gIdx]->geometry()->vertexData();
+ for (int i=0; i < count; i++){
+ int spriteIdx = m_idxStarts[gIdx] + i;
+ UltraVertices &p = particles[i];
+ int curIdx = m_spriteEngine->spriteState(spriteIdx);
+ if (curIdx != p.v1.animIdx){
+ p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = curIdx;
+ p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
+ p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(spriteIdx);
+ p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
+ }
}
}
}else{
@@ -815,43 +834,45 @@ void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d)
//TODO: get index for reload - or make function take an index
}
-void QSGImageParticle::initialize(int idx)
+void QSGImageParticle::initialize(int gIdx, int pIdx)
{
Color4ub color;
+ QSGParticleData* datum = m_system->m_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;
- switch(perfLevel){//Fall-through is intended on all of them
+ int spriteIdx = m_idxStarts[gIdx] + datum->index;
+ switch (perfLevel){//Fall-through is intended on all of them
case Sprites:
// Initial Sprite State
- m_data[idx]->animT = m_data[idx]->t;
- m_data[idx]->animIdx = 0;
- if(m_spriteEngine){
- m_spriteEngine->startSprite(idx);
- m_data[idx]->frameCount = m_spriteEngine->spriteFrames(idx);
- m_data[idx]->frameDuration = m_spriteEngine->spriteDuration(idx);
+ datum->animT = datum->t;
+ datum->animIdx = 0;
+ if (m_spriteEngine){
+ m_spriteEngine->startSprite(spriteIdx);
+ datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
+ datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
}else{
- m_data[idx]->frameCount = 1;
- m_data[idx]->frameDuration = 9999;
+ datum->frameCount = 1;
+ datum->frameDuration = 9999;
}
case Tabled:
case Deformable:
//Initial Rotation
- if(m_xVector){
- const QPointF &ret = m_xVector->sample(QPointF(m_data[idx]->x, m_data[idx]->y));
- m_data[idx]->xx = ret.x();
- m_data[idx]->xy = ret.y();
+ if (m_xVector){
+ const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
+ datum->xx = ret.x();
+ datum->xy = ret.y();
}
- if(m_yVector){
- const QPointF &ret = m_yVector->sample(QPointF(m_data[idx]->x, m_data[idx]->y));
- m_data[idx]->yx = ret.x();
- m_data[idx]->yy = ret.y();
+ if (m_yVector){
+ const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
+ datum->yx = ret.x();
+ datum->yy = ret.y();
}
- m_data[idx]->rotation =
+ datum->rotation =
(m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
- m_data[idx]->rotationSpeed =
+ datum->rotationSpeed =
(m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
- m_data[idx]->autoRotate = m_autoRotation?1.0:0.0;
+ datum->autoRotate = m_autoRotation?1.0:0.0;
case Colored:
//Color initialization
// Particle color
@@ -859,74 +880,78 @@ void QSGImageParticle::initialize(int idx)
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;
- m_data[idx]->color = color;
+ datum->color = color;
default:
break;
}
}
-void QSGImageParticle::reload(int idx)
+void QSGImageParticle::commit(int gIdx, int pIdx)
{
- if(!m_node)
+ if (m_pleaseReset)
+ return;
+ QSGGeometryNode *node = m_nodes[gIdx];
+ if (!node)
return;
+ QSGParticleData* datum = m_system->m_groupData[gIdx]->data[pIdx];
- m_node->setFlag(QSGNode::OwnsGeometry, false);
- UltraVertex *ultraVertices = (UltraVertex *) m_node->geometry()->vertexData();
- SimpleVertex *simpleVertices = (SimpleVertex *) m_node->geometry()->vertexData();
- switch(perfLevel){
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ UltraVertex *ultraVertices = (UltraVertex *) node->geometry()->vertexData();
+ SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
+ switch (perfLevel){
case Sprites:
- ultraVertices += idx*4;
- for(int i=0; i<4; i++){
- ultraVertices[i].x = m_data[idx]->x - m_systemOffset.x();
- ultraVertices[i].y = m_data[idx]->y - m_systemOffset.y();
- ultraVertices[i].t = m_data[idx]->t;
- ultraVertices[i].lifeSpan = m_data[idx]->lifeSpan;
- ultraVertices[i].size = m_data[idx]->size;
- ultraVertices[i].endSize = m_data[idx]->endSize;
- ultraVertices[i].sx = m_data[idx]->sx;
- ultraVertices[i].sy = m_data[idx]->sy;
- ultraVertices[i].ax = m_data[idx]->ax;
- ultraVertices[i].ay = m_data[idx]->ay;
- ultraVertices[i].xx = m_data[idx]->xx;
- ultraVertices[i].xy = m_data[idx]->xy;
- ultraVertices[i].yx = m_data[idx]->yx;
- ultraVertices[i].yy = m_data[idx]->yy;
- ultraVertices[i].rotation = m_data[idx]->rotation;
- ultraVertices[i].rotationSpeed = m_data[idx]->rotationSpeed;
- ultraVertices[i].autoRotate = m_data[idx]->autoRotate;
- ultraVertices[i].animIdx = m_data[idx]->animIdx;
- ultraVertices[i].frameDuration = m_data[idx]->frameDuration;
- ultraVertices[i].frameCount = m_data[idx]->frameCount;
- ultraVertices[i].animT = m_data[idx]->animT;
- ultraVertices[i].color.r = m_data[idx]->color.r;
- ultraVertices[i].color.g = m_data[idx]->color.g;
- ultraVertices[i].color.b = m_data[idx]->color.b;
- ultraVertices[i].color.a = m_data[idx]->color.a;
+ ultraVertices += pIdx*4;
+ for (int i=0; i<4; i++){
+ ultraVertices[i].x = datum->x - m_systemOffset.x();
+ ultraVertices[i].y = datum->y - m_systemOffset.y();
+ ultraVertices[i].t = datum->t;
+ ultraVertices[i].lifeSpan = datum->lifeSpan;
+ ultraVertices[i].size = datum->size;
+ ultraVertices[i].endSize = datum->endSize;
+ ultraVertices[i].sx = datum->sx;
+ ultraVertices[i].sy = datum->sy;
+ ultraVertices[i].ax = datum->ax;
+ ultraVertices[i].ay = datum->ay;
+ ultraVertices[i].xx = datum->xx;
+ ultraVertices[i].xy = datum->xy;
+ ultraVertices[i].yx = datum->yx;
+ ultraVertices[i].yy = datum->yy;
+ ultraVertices[i].rotation = datum->rotation;
+ ultraVertices[i].rotationSpeed = datum->rotationSpeed;
+ ultraVertices[i].autoRotate = datum->autoRotate;
+ ultraVertices[i].animIdx = datum->animIdx;
+ ultraVertices[i].frameDuration = datum->frameDuration;
+ ultraVertices[i].frameCount = datum->frameCount;
+ ultraVertices[i].animT = datum->animT;
+ ultraVertices[i].color.r = datum->color.r;
+ ultraVertices[i].color.g = datum->color.g;
+ ultraVertices[i].color.b = datum->color.b;
+ ultraVertices[i].color.a = datum->color.a;
}
break;
case Tabled://TODO: Us
case Deformable:
case Colored:
case Simple:
- simpleVertices += idx*4;
- for(int i=0; i<4; i++){
- simpleVertices[i].x = m_data[idx]->x - m_systemOffset.x();
- simpleVertices[i].y = m_data[idx]->y - m_systemOffset.y();
- simpleVertices[i].t = m_data[idx]->t;
- simpleVertices[i].lifeSpan = m_data[idx]->lifeSpan;
- simpleVertices[i].size = m_data[idx]->size;
- simpleVertices[i].endSize = m_data[idx]->endSize;
- simpleVertices[i].sx = m_data[idx]->sx;
- simpleVertices[i].sy = m_data[idx]->sy;
- simpleVertices[i].ax = m_data[idx]->ax;
- simpleVertices[i].ay = m_data[idx]->ay;
+ simpleVertices += pIdx*4;
+ for (int i=0; i<4; 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].sx = datum->sx;
+ simpleVertices[i].sy = datum->sy;
+ simpleVertices[i].ax = datum->ax;
+ simpleVertices[i].ay = datum->ay;
}
break;
default:
break;
}
- m_node->setFlag(QSGNode::OwnsGeometry, true);
+ node->setFlag(QSGNode::OwnsGeometry, true);
}
diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h
index dc79c5910e..6cade7f554 100644
--- a/src/declarative/particles/qsgimageparticle_p.h
+++ b/src/declarative/particles/qsgimageparticle_p.h
@@ -284,13 +284,13 @@ public slots:
protected:
void reset();
- virtual void initialize(int idx);
- virtual void reload(int idx);
+ virtual void initialize(int gIdx, int pIdx);
+ virtual void commit(int gIdx, int pIdx);
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
void prepareNextFrame();
- QSGGeometryNode* buildParticleNode();
- QSGGeometryNode* buildSimpleParticleNode();
+ QSGGeometryNode* buildParticleNodes();
+ QSGGeometryNode* buildSimpleParticleNodes();
private slots:
void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
@@ -308,11 +308,13 @@ private:
qreal m_color_variation;
qreal m_particleDuration;
- QSGGeometryNode *m_node;
+ 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?
+ int m_lastIdxStart;
UltraMaterial *m_material;
// derived values...
- int m_last_particle;
qreal m_render_opacity;
qreal m_alphaVariation;
@@ -335,8 +337,6 @@ private:
PerformanceLevel perfLevel;
PerformanceLevel m_lastLevel;
- void* m_lastData;
- int m_lastCount;
};
QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp
index 5e324cd43d..4e6a91416c 100644
--- a/src/declarative/particles/qsgitemparticle.cpp
+++ b/src/declarative/particles/qsgitemparticle.cpp
@@ -74,7 +74,7 @@ void QSGItemParticle::unfreeze(QSGItem* item)
void QSGItemParticle::take(QSGItem *item, bool prioritize)
{
- if(prioritize)
+ if (prioritize)
m_pendingItems.push_front(item);
else
m_pendingItems.push_back(item);
@@ -85,54 +85,54 @@ void QSGItemParticle::give(QSGItem *item)
//TODO: This
}
-void QSGItemParticle::initialize(int idx)
+void QSGItemParticle::initialize(int gIdx, int pIdx)
{
- m_loadables << idx;//defer to other thread
+ m_loadables << m_system->m_groupData[gIdx]->data[pIdx];//defer to other thread
}
-void QSGItemParticle::reload(int idx)
+void QSGItemParticle::commit(int, int)
{
}
void QSGItemParticle::tick()
{
- foreach(QSGItem* item, m_deletables){
- if(m_fade)
+ foreach (QSGItem* item, m_deletables){
+ if (m_fade)
item->setOpacity(0.);
QSGItemParticleAttached* mpa;
- if((mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(item))))
+ if ((mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(item))))
mpa->detach();//reparent as well?
//TODO: Delete iff we created it
m_activeCount--;
}
m_deletables.clear();
- foreach(int pos, m_loadables){
- if(m_stasis.contains(m_data[pos]->delegate))
+ foreach (QSGParticleData* 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(m_data[pos]->delegate){
- m_deletables << m_data[pos]->delegate;
+ if (d->delegate){
+ m_deletables << d->delegate;
m_activeCount--;
}
- m_data[pos]->delegate = 0;
- if(!m_pendingItems.isEmpty()){
- m_data[pos]->delegate = m_pendingItems.front();
+ d->delegate = 0;
+ if (!m_pendingItems.isEmpty()){
+ d->delegate = m_pendingItems.front();
m_pendingItems.pop_front();
- }else if(m_delegate){
- m_data[pos]->delegate = qobject_cast<QSGItem*>(m_delegate->create(qmlContext(this)));
+ }else if (m_delegate){
+ d->delegate = qobject_cast<QSGItem*>(m_delegate->create(qmlContext(this)));
}
- if(m_data[pos]->delegate && m_data[pos]){//###Data can be zero if creating an item leads to a reset - this screws things up.
- m_data[pos]->delegate->setX(m_data[pos]->curX() - m_data[pos]->delegate->width()/2);//TODO: adjust for system?
- m_data[pos]->delegate->setY(m_data[pos]->curY() - m_data[pos]->delegate->height()/2);
- QSGItemParticleAttached* mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_data[pos]->delegate));
- if(mpa){
+ 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);
+ QSGItemParticleAttached* mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(d->delegate));
+ if (mpa){
mpa->m_mp = this;
mpa->attach();
}
- m_data[pos]->delegate->setParentItem(this);
- if(m_fade)
- m_data[pos]->delegate->setOpacity(0.);
+ d->delegate->setParentItem(this);
+ if (m_fade)
+ d->delegate->setOpacity(0.);
m_activeCount++;
}
}
@@ -151,14 +151,14 @@ void QSGItemParticle::reset()
QSGNode* QSGItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d)
{
//Dummy update just to get painting tick
- if(m_pleaseReset){
+ if (m_pleaseReset){
m_pleaseReset = false;
reset();
}
prepareNextFrame();
update();//Get called again
- if(n)
+ if (n)
n->markDirty(QSGNode::DirtyMaterial);
return QSGItem::updatePaintNode(n,d);
}
@@ -169,38 +169,43 @@ void QSGItemParticle::prepareNextFrame()
qreal curT = timeStamp/1000.0;
qreal dt = curT - m_lastT;
m_lastT = curT;
- if(!m_activeCount)
+ if (!m_activeCount)
return;
//TODO: Size, better fade?
- for(int i=0; i<count(); i++){
- QSGParticleData* data = m_data[i];
- QSGItem* item = data->delegate;
- if(!item)
- continue;
- qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan;
- if(m_stasis.contains(item)) {
- m_data[i]->t += dt;//Stasis effect
- continue;
- }
- if(t >= 1.0){//Usually happens from load
- m_deletables << item;
- m_data[i]->delegate = 0;
- m_activeCount--;
- }else{//Fade
- if(m_fade){
- qreal o = 1.;
- if(t<0.2)
- o = t*5;
- if(t>0.8)
- o = (1-t)*5;
- item->setOpacity(o);
- }else{
- item->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on
+ foreach (const QString &str, m_particles){
+ int gIdx = m_system->m_groupIds[str];
+ int count = m_system->m_groupData[gIdx]->size();
+
+ for (int i=0; i<count; i++){
+ QSGParticleData* data = m_system->m_groupData[gIdx]->data[i];
+ QSGItem* 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;
+ m_activeCount--;
+ }else{//Fade
+ if (m_fade){
+ qreal o = 1.;
+ if (t<0.2)
+ o = t*5;
+ if (t>0.8)
+ o = (1-t)*5;
+ item->setOpacity(o);
+ }else{
+ item->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on
+ }
}
+ item->setX(data->curX() - item->width()/2 - m_systemOffset.x());
+ item->setY(data->curY() - item->height()/2 - m_systemOffset.y());
}
- item->setX(data->curX() - item->width()/2 - m_systemOffset.x());
- item->setY(data->curY() - item->height()/2 - m_systemOffset.y());
}
}
diff --git a/src/declarative/particles/qsgitemparticle_p.h b/src/declarative/particles/qsgitemparticle_p.h
index 24bbcf97d8..233f942d40 100644
--- a/src/declarative/particles/qsgitemparticle_p.h
+++ b/src/declarative/particles/qsgitemparticle_p.h
@@ -82,7 +82,7 @@ public slots:
void take(QSGItem* item,bool prioritize=false);//take by modelparticle
void give(QSGItem* item);//give from modelparticle
- void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();}
+ void setFade(bool arg){if (arg == m_fade) return; m_fade = arg; emit fadeChanged();}
void setDelegate(QDeclarativeComponent* arg)
{
if (m_delegate != arg) {
@@ -93,14 +93,14 @@ public slots:
protected:
virtual void reset();
- virtual void reload(int idx);
- virtual void initialize(int idx);
+ virtual void commit(int gIdx, int pIdx);
+ virtual void initialize(int gIdx, int pIdx);
void prepareNextFrame();
private slots:
void tick();
private:
QList<QSGItem* > m_deletables;
- QList< int > m_loadables;
+ QList< QSGParticleData* > m_loadables;
bool m_fade;
QList<QSGItem*> m_pendingItems;
diff --git a/src/declarative/particles/qsgkill.cpp b/src/declarative/particles/qsgkill.cpp
index eb3a55d402..e0406bc12b 100644
--- a/src/declarative/particles/qsgkill.cpp
+++ b/src/declarative/particles/qsgkill.cpp
@@ -51,9 +51,10 @@ QSGKillAffector::QSGKillAffector(QSGItem *parent) :
bool QSGKillAffector::affectParticle(QSGParticleData *d, qreal dt)
{
Q_UNUSED(dt);
- if(d->stillAlive()){
+ if (d->stillAlive()){
d->t -= d->lifeSpan + 1;
return true;
}
+ return false;
}
QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsglineextruder.cpp b/src/declarative/particles/qsglineextruder.cpp
index f32b01402a..d5f32612b1 100644
--- a/src/declarative/particles/qsglineextruder.cpp
+++ b/src/declarative/particles/qsglineextruder.cpp
@@ -49,16 +49,16 @@ QSGLineExtruder::QSGLineExtruder(QObject *parent) :
QPointF QSGLineExtruder::extrude(const QRectF &r)
{
qreal x,y;
- if(!r.height()){
+ if (!r.height()){
x = r.width() * ((qreal)rand())/RAND_MAX;
y = 0;
}else{
y = r.height() * ((qreal)rand())/RAND_MAX;
- if(!r.width()){
+ if (!r.width()){
x = 0;
}else{
x = r.width()/r.height() * y;
- if(m_mirrored)
+ if (m_mirrored)
x = r.width() - x;
}
}
diff --git a/src/declarative/particles/qsgmaskextruder.cpp b/src/declarative/particles/qsgmaskextruder.cpp
index d9a463910d..5e60d31bcf 100644
--- a/src/declarative/particles/qsgmaskextruder.cpp
+++ b/src/declarative/particles/qsgmaskextruder.cpp
@@ -53,7 +53,7 @@ QSGMaskExtruder::QSGMaskExtruder(QObject *parent) :
QPointF QSGMaskExtruder::extrude(const QRectF &r)
{
ensureInitialized(r);
- if(!m_mask.count() || m_img.isNull())
+ 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?
@@ -63,7 +63,7 @@ QPointF QSGMaskExtruder::extrude(const QRectF &r)
bool QSGMaskExtruder::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())
+ if (m_img.isNull())
return false;
QPoint p = point.toPoint() - bounds.topLeft().toPoint();
return m_img.rect().contains(p) && (bool)m_img.pixelIndex(p);
@@ -71,26 +71,26 @@ bool QSGMaskExtruder::contains(const QRectF &bounds, const QPointF &point)
void QSGMaskExtruder::ensureInitialized(const QRectF &r)
{
- if(m_lastWidth == r.width() && m_lastHeight == r.height())
+ if (m_lastWidth == r.width() && m_lastHeight == r.height())
return;//Same as before
m_lastWidth = r.width();
m_lastHeight = r.height();
m_img = QImage();
m_mask.clear();
- if(m_source.isEmpty())
+ if (m_source.isEmpty())
return;
m_img = QImage(m_source.toLocalFile());
- if(m_img.isNull()){
+ if (m_img.isNull()){
qWarning() << "MaskShape: Cannot load" << qPrintable(m_source.toLocalFile());
return;
}
m_img = m_img.createAlphaMask();
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
+ 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);
}
}
diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp
index 47b8844ad5..be10f33255 100644
--- a/src/declarative/particles/qsgmodelparticle.cpp
+++ b/src/declarative/particles/qsgmodelparticle.cpp
@@ -66,21 +66,21 @@ QVariant QSGModelParticle::model() const
void QSGModelParticle::setModel(const QVariant &arg)
{
- if(arg == m_dataSource)
+ if (arg == m_dataSource)
return;
m_dataSource = arg;
- if(qobject_cast<QSGVisualDataModel*>(arg.value<QObject*>())) {
- if(m_ownModel && m_model)
+ if (qobject_cast<QSGVisualDataModel*>(arg.value<QObject*>())) {
+ if (m_ownModel && m_model)
delete m_model;
m_model = qobject_cast<QSGVisualDataModel*>(arg.value<QObject*>());
m_ownModel = false;
}else{
- if(!m_model || !m_ownModel)
+ if (!m_model || !m_ownModel)
m_model = new QSGVisualDataModel(qmlContext(this));
m_model->setModel(m_dataSource);
m_ownModel = true;
}
- if(m_comp)
+ if (m_comp)
m_model->setDelegate(m_comp);
emit modelChanged();
emit modelCountChanged();
@@ -94,19 +94,19 @@ void QSGModelParticle::setModel(const QVariant &arg)
void QSGModelParticle::updateCount()
{
int newCount = 0;
- if(m_model)
+ if (m_model)
newCount = m_model->count();
- if(newCount < 0)
+ if (newCount < 0)
return;//WTF?
- if(m_modelCount == 0 || newCount == 0){
+ if (m_modelCount == 0 || newCount == 0){
m_available.clear();
- for(int i=0; i<newCount; i++)
+ for (int i=0; i<newCount; i++)
m_available << i;
- }else if(newCount < m_modelCount){
- for(int i=newCount; i<m_modelCount; i++) //existing ones must leave normally, but aren't readded
+ }else if (newCount < m_modelCount){
+ for (int i=newCount; i<m_modelCount; i++) //existing ones must leave normally, but aren't readded
m_available.removeAll(i);
- }else if(newCount > m_modelCount){
- for(int i=m_modelCount; i<newCount; i++)
+ }else if (newCount > m_modelCount){
+ for (int i=m_modelCount; i<newCount; i++)
m_available << i;
}
m_modelCount = newCount;
@@ -114,7 +114,7 @@ void QSGModelParticle::updateCount()
QDeclarativeComponent *QSGModelParticle::delegate() const
{
- if(m_model)
+ if (m_model)
return m_model->delegate();
return 0;
}
@@ -125,14 +125,14 @@ void QSGModelParticle::setDelegate(QDeclarativeComponent *comp)
if (comp == dataModel->delegate())
return;
m_comp = comp;
- if(m_model)
+ if (m_model)
m_model->setDelegate(comp);
emit delegateChanged();
}
int QSGModelParticle::modelCount() const
{
- if(m_model)
+ if (m_model)
const_cast<QSGModelParticle*>(this)->updateCount();//TODO: Investigate why this doesn't get called properly
return m_modelCount;
}
@@ -149,53 +149,54 @@ void QSGModelParticle::unfreeze(QSGItem* item)
m_stasis.remove(item);
}
-void QSGModelParticle::initialize(int idx)
+void QSGModelParticle::initialize(int gIdx, int pIdx)
{
- if(!m_model || !m_model->count())
+ if (!m_model || !m_model->count())
return;
- if(m_available.isEmpty())
+ if (m_available.isEmpty())
return;
- m_requests << idx;
+ m_requests << m_system->m_groupData[gIdx]->data[pIdx];
m_activeCount++;
}
void QSGModelParticle::processPending()
{//can't create/delete arbitrary items in the render thread
- foreach(QSGItem* item, m_deletables){
+ foreach (QSGItem* item, m_deletables){
item->setOpacity(0.);
m_model->release(item);
}
m_deletables.clear();
- foreach(int pos, m_requests){
- if(m_data[pos]->delegate){
- if(m_stasis.contains(m_data[pos]->delegate))
+ foreach (QSGParticleData* datum, m_requests){
+ if (datum->delegate){
+ if (m_stasis.contains(datum->delegate))
qWarning() << "Current model particles prefers overwrite:false";
//remove old item from the particle that is dying to make room for this one
- m_deletables << m_data[pos]->delegate;
- m_available << m_data[pos]->modelIndex;
- m_data[pos]->modelIndex = -1;
- m_data[pos]->delegate = 0;
- m_data[pos] = 0;
+ m_deletables << datum->delegate;
+ m_available << datum->modelIndex;
+ datum->modelIndex = -1;
+ datum->delegate = 0;
+ datum = 0;
m_activeCount--;
}
- if(!m_available.isEmpty()){
- m_data[pos]->delegate = m_model->item(m_available.first());
- m_data[pos]->modelIndex = m_available.first();
+ if (!m_available.isEmpty()){
+ datum->delegate = m_model->item(m_available.first());
+ datum->modelIndex = m_available.first();
m_available.pop_front();
- QSGModelParticleAttached* mpa = qobject_cast<QSGModelParticleAttached*>(qmlAttachedPropertiesObject<QSGModelParticle>(m_data[pos]->delegate));
- if(mpa){
+ QSGModelParticleAttached* mpa = qobject_cast<QSGModelParticleAttached*>(qmlAttachedPropertiesObject<QSGModelParticle>(datum->delegate));
+ if (mpa){
mpa->m_mp = this;
mpa->attach();
}
- m_data[pos]->delegate->setParentItem(this);
+ datum->delegate->setParentItem(this);
+ datum->delegate->setOpacity(0.0);
}
}
m_requests.clear();
}
-void QSGModelParticle::reload(int idx)
+void QSGModelParticle::commit(int gIdx, int pIdx)
{
//No-op unless we start copying the data.
}
@@ -212,14 +213,14 @@ void QSGModelParticle::reset()
QSGNode* QSGModelParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d)
{
//Dummy update just to get painting tick
- if(m_pleaseReset){
+ if (m_pleaseReset){
m_pleaseReset = false;
reset();
}
prepareNextFrame();
update();//Get called again
- if(n)
+ if (n)
n->markDirty(QSGNode::DirtyMaterial);
return QSGItem::updatePaintNode(n,d);
}
@@ -230,40 +231,45 @@ void QSGModelParticle::prepareNextFrame()
qreal curT = timeStamp/1000.0;
qreal dt = curT - m_lastT;
m_lastT = curT;
- if(!m_activeCount)
+ if (!m_activeCount)
return;
//TODO: Size, better fade?
- for(int i=0; i<count(); i++){
- QSGParticleData* data = m_data[i];
- if(!data->delegate)
- continue;
- qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan;
- if(m_stasis.contains(m_data[i]->delegate)) {
- m_data[i]->t += dt;//Stasis effect
- continue;
- }
- if(t >= 1.0){//Usually happens from load
- m_available << m_data[i]->modelIndex;
- m_deletables << m_data[i]->delegate;
- m_data[i]->modelIndex = -1;
- m_data[i]->delegate = 0;
- m_data[i] = 0;
- m_activeCount--;
- }else{//Fade
- if(m_fade){
- qreal o = 1.;
- if(t<0.2)
- o = t*5;
- if(t>0.8)
- o = (1-t)*5;
- m_data[i]->delegate->setOpacity(o);
- }else{
- m_data[i]->delegate->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on
+ foreach (const QString &str, m_particles){
+ int gIdx = m_system->m_groupIds[str];
+ int count = m_system->m_groupData[gIdx]->size();
+
+ for (int i=0; i<count; i++){
+ QSGParticleData* data = m_system->m_groupData[gIdx]->data[i];
+ if (!data || !data->delegate)
+ continue;
+ qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan;
+ if (m_stasis.contains(data->delegate)) {
+ data->t += dt;//Stasis effect
+ continue;
+ }
+ if (t >= 1.0){//Usually happens from load
+ m_available << data->modelIndex;
+ m_deletables << data->delegate;
+ data->modelIndex = -1;
+ data->delegate = 0;
+ m_activeCount--;
+ continue;
+ }else{//Fade
+ if (m_fade){
+ qreal o = 1.;
+ if (t<0.2)
+ o = t*5;
+ if (t>0.8)
+ o = (1-t)*5;
+ data->delegate->setOpacity(o);
+ }else{
+ data->delegate->setOpacity(1.);//###Without fade, it's just a binary toggle - if we turn it off we have to turn it back on
+ }
}
+ data->delegate->setX(data->curX() - data->delegate->width()/2 - m_systemOffset.x());
+ data->delegate->setY(data->curY() - data->delegate->height()/2 - m_systemOffset.y());
}
- m_data[i]->delegate->setX(data->curX() - m_data[i]->delegate->width()/2 - m_systemOffset.x());
- m_data[i]->delegate->setY(data->curY() - m_data[i]->delegate->height()/2 - m_systemOffset.y());
}
}
diff --git a/src/declarative/particles/qsgmodelparticle_p.h b/src/declarative/particles/qsgmodelparticle_p.h
index 31e4025bb4..aba64e4841 100644
--- a/src/declarative/particles/qsgmodelparticle_p.h
+++ b/src/declarative/particles/qsgmodelparticle_p.h
@@ -86,11 +86,11 @@ public slots:
void freeze(QSGItem* item);
void unfreeze(QSGItem* item);
- void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();}
+ void setFade(bool arg){if (arg == m_fade) return; m_fade = arg; emit fadeChanged();}
protected:
virtual void reset();
- virtual void reload(int idx);
- virtual void initialize(int idx);
+ virtual void commit(int gIdx, int pIdx);
+ virtual void initialize(int gIdx, int pIdx);
void prepareNextFrame();
private slots:
void updateCount();
@@ -101,7 +101,7 @@ private:
QSGVisualDataModel *m_model;
QVariant m_dataSource;
QList<QSGItem*> m_deletables;
- QList< int > m_requests;
+ QList< QSGParticleData* > m_requests;
bool m_fade;
QList<QSGItem*> m_pendingItems;
@@ -115,7 +115,7 @@ private:
class QSGModelParticleAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QSGModelParticle* particle READ particle CONSTANT);
+ Q_PROPERTY(QSGModelParticle* particle READ particle CONSTANT)
public:
QSGModelParticleAttached(QObject* parent)
: QObject(parent), m_mp(0)
diff --git a/src/declarative/particles/qsgparticleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp
index 5b0936cc40..ccb12659fb 100644
--- a/src/declarative/particles/qsgparticleaffector.cpp
+++ b/src/declarative/particles/qsgparticleaffector.cpp
@@ -56,44 +56,45 @@ QSGParticleAffector::QSGParticleAffector(QSGItem *parent) :
void QSGParticleAffector::componentComplete()
{
- if(!m_system && qobject_cast<QSGParticleSystem*>(parentItem()))
+ if (!m_system && qobject_cast<QSGParticleSystem*>(parentItem()))
setSystem(qobject_cast<QSGParticleSystem*>(parentItem()));
- if(!m_system)
+ if (!m_system)
qWarning() << "Affector created without a particle system specified";//TODO: useful QML warnings, like line number?
QSGItem::componentComplete();
}
void QSGParticleAffector::affectSystem(qreal dt)
{
- if(!m_active)
+ if (!m_active)
return;
- if(!m_system){
- qDebug() << "No system" << this;
- return;
- }
//If not reimplemented, calls affect particle per particle
//But only on particles in targeted system/area
- if(m_updateIntSet){
+ if (m_updateIntSet){
m_groups.clear();
- foreach(const QString &p, m_particles)
+ foreach (const QString &p, m_particles)
m_groups << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned?
m_updateIntSet = false;
}
- foreach(QSGParticleGroupData* gd, m_system->m_groupData){
- foreach(QSGParticleData* d, gd->data){
- if(!d || (m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index))))
+ foreach (QSGParticleGroupData* gd, m_system->m_groupData){
+ foreach (QSGParticleData* d, gd->data){
+ if (!d)
continue;
- if(m_groups.isEmpty() || m_groups.contains(d->group)){
+ if (m_groups.isEmpty() || m_groups.contains(d->group)){
+ if ((m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index)))
+ || !d->stillAlive())
+ continue;
//Need to have previous location for affected. if signal || shape might be faster?
QPointF curPos = QPointF(d->curX(), d->curY());
- if(width() == 0 || height() == 0
+ if (width() == 0 || height() == 0
|| m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()),curPos)){
- if(affectParticle(d, dt)){
- m_system->m_needsReset << d;
- if(m_onceOff)
- m_onceOffed << qMakePair(d->group, d->index);
- if(m_signal)
- emit affected(curPos.x(), curPos.y());
+ if (m_collisionParticles.isEmpty() || isColliding(d)){
+ if (affectParticle(d, dt)){
+ m_system->m_needsReset << d;
+ if (m_onceOff)
+ m_onceOffed << qMakePair(d->group, d->index);
+ if (m_signal)
+ emit affected(curPos.x(), curPos.y());
+ }
}
}
}
@@ -105,19 +106,42 @@ bool QSGParticleAffector::affectParticle(QSGParticleData *d, qreal dt)
{
Q_UNUSED(d);
Q_UNUSED(dt);
- return false;
+ return m_signal;//If signalling, then we always 'null affect' it.
}
void QSGParticleAffector::reset(QSGParticleData* pd)
{//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
- if(m_onceOff)
- m_onceOffed.remove(qMakePair(pd->group, pd->index));
+ if (m_onceOff)
+ if (m_groups.isEmpty() || m_groups.contains(pd->group))
+ m_onceOffed.remove(qMakePair(pd->group, pd->index));
}
void QSGParticleAffector::updateOffsets()
{
- if(m_system)
+ if (m_system)
m_offset = m_system->mapFromItem(this, QPointF(0, 0));
}
+bool QSGParticleAffector::isColliding(QSGParticleData *d)
+{
+ qreal myCurX = d->curX();
+ qreal myCurY = d->curY();
+ qreal myCurSize = d->curSize()/2;
+ foreach (const QString &group, m_collisionParticles){
+ foreach (QSGParticleData* other, m_system->m_groupData[m_system->m_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/declarative/particles/qsgparticleaffector_p.h b/src/declarative/particles/qsgparticleaffector_p.h
index 7418760941..806a1cce1a 100644
--- a/src/declarative/particles/qsgparticleaffector_p.h
+++ b/src/declarative/particles/qsgparticleaffector_p.h
@@ -57,10 +57,11 @@ class QSGParticleAffector : public QSGItem
Q_OBJECT
Q_PROPERTY(QSGParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
Q_PROPERTY(QStringList particles READ particles WRITE setParticles NOTIFY particlesChanged)
+ Q_PROPERTY(QStringList collisionParticles READ collisionParticles WRITE setCollisionParticles NOTIFY collisionParticlesChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(bool onceOff READ onceOff WRITE setOnceOff NOTIFY onceOffChanged)
Q_PROPERTY(QSGParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged)
- Q_PROPERTY(bool signal READ signal WRITE setSignal NOTIFY signalChanged)
+ Q_PROPERTY(bool signal READ signal WRITE setSignal NOTIFY signalChanged)//TODO: Determine by whether it's connected
public:
explicit QSGParticleAffector(QSGItem *parent = 0);
@@ -96,6 +97,11 @@ public:
return m_signal;
}
+ QStringList collisionParticles() const
+ {
+ return m_collisionParticles;
+ }
+
signals:
void systemChanged(QSGParticleSystem* arg);
@@ -111,6 +117,8 @@ signals:
void affected(qreal x, qreal y);
void signalChanged(bool arg);
+ void collisionParticlesChanged(QStringList arg);
+
public slots:
void setSystem(QSGParticleSystem* arg)
{
@@ -142,6 +150,7 @@ void setOnceOff(bool arg)
{
if (m_onceOff != arg) {
m_onceOff = arg;
+ m_needsReset = true;
emit onceOffChanged(arg);
}
}
@@ -162,6 +171,14 @@ void setSignal(bool arg)
}
}
+void setCollisionParticles(QStringList arg)
+{
+ if (m_collisionParticles != arg) {
+ m_collisionParticles = arg;
+ emit collisionParticlesChanged(arg);
+ }
+}
+
protected:
friend class QSGParticleSystem;
virtual bool affectParticle(QSGParticleData *d, qreal dt);
@@ -183,6 +200,9 @@ private:
bool m_signal;
+ QStringList m_collisionParticles;
+
+ bool isColliding(QSGParticleData* d);
private slots:
void updateOffsets();
};
diff --git a/src/declarative/particles/qsgparticleemitter.cpp b/src/declarative/particles/qsgparticleemitter.cpp
index 143338f798..a75d6388ed 100644
--- a/src/declarative/particles/qsgparticleemitter.cpp
+++ b/src/declarative/particles/qsgparticleemitter.cpp
@@ -70,15 +70,15 @@ QSGParticleEmitter::QSGParticleEmitter(QSGItem *parent) :
QSGParticleEmitter::~QSGParticleEmitter()
{
- if(m_defaultExtruder)
+ if (m_defaultExtruder)
delete m_defaultExtruder;
}
void QSGParticleEmitter::componentComplete()
{
- if(!m_system && qobject_cast<QSGParticleSystem*>(parentItem()))
+ if (!m_system && qobject_cast<QSGParticleSystem*>(parentItem()))
setSystem(qobject_cast<QSGParticleSystem*>(parentItem()));
- if(!m_system)
+ if (!m_system)
qWarning() << "Emitter created without a particle system specified";//TODO: useful QML warnings, like line number?
QSGItem::componentComplete();
}
@@ -99,31 +99,31 @@ void QSGParticleEmitter::setEmitting(bool arg)
QSGParticleExtruder* QSGParticleEmitter::effectiveExtruder()
{
- if(m_extruder)
+ if (m_extruder)
return m_extruder;
- if(!m_defaultExtruder)
+ if (!m_defaultExtruder)
m_defaultExtruder = new QSGParticleExtruder;
return m_defaultExtruder;
}
void QSGParticleEmitter::pulse(qreal seconds)
{
- if(!particleCount())
+ if (!particleCount())
qWarning() << "pulse called on an emitter with a particle count of zero";
- if(!m_emitting)
+ if (!m_emitting)
m_burstLeft = seconds*1000.0;//TODO: Change name to match
}
void QSGParticleEmitter::burst(int num)
{
- if(!particleCount())
+ if (!particleCount())
qWarning() << "burst called on an emitter with a particle count of zero";
m_burstQueue << qMakePair(num, QPointF(x(), y()));
}
void QSGParticleEmitter::burst(int num, qreal x, qreal y)
{
- if(!particleCount())
+ if (!particleCount())
qWarning() << "burst called on an emitter with a particle count of zero";
m_burstQueue << qMakePair(num, QPointF(x, y));
}
@@ -131,12 +131,12 @@ void QSGParticleEmitter::burst(int num, qreal x, qreal y)
void QSGParticleEmitter::setMaxParticleCount(int arg)
{
if (m_maxParticleCount != arg) {
- if(arg < 0 && m_maxParticleCount >= 0){
+ 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){
+ }else if (arg >= 0 && m_maxParticleCount < 0){
disconnect(this, SIGNAL(particlesPerSecondChanged(qreal)),
this, SIGNAL(particleCountChanged()));
disconnect(this, SIGNAL(particleDurationChanged(int)),
@@ -149,7 +149,7 @@ void QSGParticleEmitter::setMaxParticleCount(int arg)
int QSGParticleEmitter::particleCount() const
{
- if(m_maxParticleCount >= 0)
+ if (m_maxParticleCount >= 0)
return m_maxParticleCount;
return m_particlesPerSecond*((m_particleDuration+m_particleDurationVariation)/1000.0);
}
diff --git a/src/declarative/particles/qsgparticleextruder.cpp b/src/declarative/particles/qsgparticleextruder.cpp
index 91b968c8af..f0658f67e5 100644
--- a/src/declarative/particles/qsgparticleextruder.cpp
+++ b/src/declarative/particles/qsgparticleextruder.cpp
@@ -50,11 +50,11 @@ QSGParticleExtruder::QSGParticleExtruder(QObject *parent) :
QPointF QSGParticleExtruder::extrude(const QRectF &rect)
{
- if(m_fill)
+ 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?
+ switch (side){//TODO: Doesn't this overlap the corners?
case 0:
return QPointF(rect.x(),
((qreal)rand() / RAND_MAX) * rect.height() + rect.y());
diff --git a/src/declarative/particles/qsgparticlepainter.cpp b/src/declarative/particles/qsgparticlepainter.cpp
index 34313bd4fb..a7a05ed631 100644
--- a/src/declarative/particles/qsgparticlepainter.cpp
+++ b/src/declarative/particles/qsgparticlepainter.cpp
@@ -44,8 +44,10 @@
QT_BEGIN_NAMESPACE
QSGParticlePainter::QSGParticlePainter(QSGItem *parent) :
QSGItem(parent),
- m_system(0), m_count(0), m_lastStart(0), m_sentinel(new QSGParticleData)
+ m_system(0), m_count(0), m_sentinel(new QSGParticleData(0))
{
+ connect(this, SIGNAL(parentChanged(QSGItem*)),
+ this, SLOT(calcSystemOffset()));
connect(this, SIGNAL(xChanged()),
this, SLOT(calcSystemOffset()));
connect(this, SIGNAL(yChanged()),
@@ -54,9 +56,9 @@ QSGParticlePainter::QSGParticlePainter(QSGItem *parent) :
void QSGParticlePainter::componentComplete()
{
- if(!m_system && qobject_cast<QSGParticleSystem*>(parentItem()))
+ if (!m_system && qobject_cast<QSGParticleSystem*>(parentItem()))
setSystem(qobject_cast<QSGParticleSystem*>(parentItem()));
- if(!m_system)
+ if (!m_system)
qWarning() << "ParticlePainter created without a particle system specified";//TODO: useful QML warnings, like line number?
QSGItem::componentComplete();
}
@@ -66,7 +68,7 @@ void QSGParticlePainter::setSystem(QSGParticleSystem *arg)
{
if (m_system != arg) {
m_system = arg;
- if(m_system){
+ if (m_system){
m_system->registerParticlePainter(this);
connect(m_system, SIGNAL(xChanged()),
this, SLOT(calcSystemOffset()));
@@ -80,80 +82,28 @@ void QSGParticlePainter::setSystem(QSGParticleSystem *arg)
void QSGParticlePainter::load(QSGParticleData* d)
{
- int idx = particleTypeIndex(d);
- m_data[idx] = d;
- initialize(idx);
- reload(idx);
+ initialize(d->group, d->index);
+ commit(d->group, d->index);
}
void QSGParticlePainter::reload(QSGParticleData* d)
{
- reload(particleTypeIndex(d));
+ commit(d->group, d->index);
}
void QSGParticlePainter::reset()
{
- //Have to every time because what it's emitting may have changed and that affects particleTypeIndex
- if(m_system && !m_inResize)
- resize(0,1);//###Fix this by making resize take sensible arguments
- //###This also means double resets. Make reset not virtual?
+ calcSystemOffset(true);//In case an ancestor changed in some way
}
-void QSGParticlePainter::resize(int oldSize, int newSize)
-{
- if(newSize == oldSize)//TODO: What if particles switched so indices change but total count is the same?
- return;
-
- QHash<int, QPair<int, int> > oldStarts(m_particleStarts);
- //Update particle starts datastore
- m_particleStarts.clear();
- m_lastStart = 0;
- QList<int> particleList;
- if(m_particles.isEmpty())
- particleList << 0;
- foreach(const QString &s, m_particles)
- particleList << m_system->m_groupIds[s];
- foreach(int gIdx, particleList){
- QSGParticleGroupData *gd = m_system->m_groupData[gIdx];
- m_particleStarts.insert(gIdx, qMakePair<int, int>(gd->size, m_lastStart));
- m_lastStart += gd->size;
- }
-
- //Shuffle stuff around
- //TODO: In place shuffling because it's faster
- QVector<QSGParticleData*> oldData(m_data);
- QVector<QObject*> oldAttached(m_attachedData);
- m_data.clear();
- m_data.resize(m_count);
- m_attachedData.resize(m_count);
- foreach(int gIdx, particleList){
- QSGParticleGroupData *gd = m_system->m_groupData[gIdx];
- for(int i=0; i<gd->data.size(); i++){//TODO: When group didn't exist before
- int newIdx = m_particleStarts[gIdx].second + i;
- int oldIdx = oldStarts[gIdx].second + i;
- if(i >= oldStarts[gIdx].first || oldData.size() <= oldIdx){
- m_data[newIdx] = m_sentinel;
- }else{
- m_data[newIdx] = oldData[oldIdx];
- m_attachedData[newIdx] = oldAttached[oldIdx];
- }
- }
- }
- m_inResize = true;
- reset();
- m_inResize = false;
-}
-
-
-void QSGParticlePainter::setCount(int c)
+void QSGParticlePainter::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)
+ if (c == m_count)
return;
- int lastCount = m_count;
m_count = c;
- resize(lastCount, m_count);
emit countChanged();
+ reset();
}
int QSGParticlePainter::count()
@@ -161,26 +111,17 @@ int QSGParticlePainter::count()
return m_count;
}
-int QSGParticlePainter::particleTypeIndex(QSGParticleData* d)
-{
- Q_ASSERT(d && m_particleStarts.contains(d->group));//XXX
- int ret = m_particleStarts[d->group].second + d->index;
- Q_ASSERT(ret >=0 && ret < m_count);//XXX:shouldn't assert, but bugs here were hard to find in the past
- return ret;
-}
-
-
-void QSGParticlePainter::calcSystemOffset()
+void QSGParticlePainter::calcSystemOffset(bool resetPending)
{
if (!m_system || !parentItem())
return;
QPointF lastOffset = m_systemOffset;
- m_systemOffset = -1 * this->mapFromItem(m_system, QPointF());
- if(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_particles){
+ foreach (const QString &g, m_particles){
int gId = m_system->m_groupIds[g];
- foreach(QSGParticleData* d, m_system->m_groupData[gId]->data)
+ foreach (QSGParticleData* d, m_system->m_groupData[gId]->data)
reload(d);
}
}
diff --git a/src/declarative/particles/qsgparticlepainter_p.h b/src/declarative/particles/qsgparticlepainter_p.h
index e506018f53..49f7881a0a 100644
--- a/src/declarative/particles/qsgparticlepainter_p.h
+++ b/src/declarative/particles/qsgparticlepainter_p.h
@@ -94,7 +94,7 @@ void setParticles(QStringList arg)
}
}
private slots:
- void calcSystemOffset();
+ void calcSystemOffset(bool resetPending = false);
protected:
/* Reset resets all your internal data structures. But anything attached to a particle should
@@ -104,11 +104,15 @@ protected:
virtual void reset();
virtual void componentComplete();
- //Data interface to painters
- QVector<QSGParticleData*> m_data; //Actually stored in arbitrary order,
- QVector<QObject*> m_attachedData; //This data will be moved along with m_data in resizes (but you own it)
- virtual void initialize(int){}
- virtual void reload(int){}//If you need to do something on size changed, check m_data size in this? Or we reset you every time?
+ virtual void initialize(int gIdx, int pIdx){
+ Q_UNUSED(gIdx);
+ Q_UNUSED(pIdx);
+ }
+ virtual void commit(int gIdx, int pIdx){
+ //###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);
+ }
QSGParticleSystem* m_system;
friend class QSGParticleSystem;
@@ -117,16 +121,9 @@ protected:
QStringList m_particles;
QPointF m_systemOffset;
-
private:
- int m_lastStart;
- QHash<int, QPair<int, int> > m_particleStarts;
- int particleTypeIndex(QSGParticleData* d);//Now private
- void resize(int, int);
-
QSGParticleData* m_sentinel;
//QVector<QSGParticleData*> m_shadowData;//For when we implement overwrite: false
- bool m_inResize;
};
QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgparticlesmodule.cpp b/src/declarative/particles/qsgparticlesmodule.cpp
index a7a9a9253f..e736e6fd8b 100644
--- a/src/declarative/particles/qsgparticlesmodule.cpp
+++ b/src/declarative/particles/qsgparticlesmodule.cpp
@@ -65,6 +65,8 @@
#include "qsgtargeteddirection_p.h"
#include "qsgturbulence_p.h"
#include "qsgwander_p.h"
+#include "qsgtargetaffector_p.h"
+#include "qsgcumulativedirection_p.h"
QT_BEGIN_NAMESPACE
@@ -95,8 +97,9 @@ void QSGParticlesModule::defineModule()
qmlRegisterType<QSGPointDirection>(uri, 2, 0, "PointDirection");
qmlRegisterType<QSGAngledDirection>(uri, 2, 0, "AngledDirection");
qmlRegisterType<QSGTargetedDirection>(uri, 2, 0, "TargetedDirection");
+ qmlRegisterType<QSGCumulativeDirection>(uri, 2, 0, "CumulativeDirection");
- qmlRegisterType<QSGParticleAffector>(uri, 2, 0, "ParticleAffector");//if it has a triggered signal, it's useful
+ qmlRegisterType<QSGParticleAffector>(uri, 2, 0, "Affector");//for the triggered signal
qmlRegisterType<QSGWanderAffector>(uri, 2, 0, "Wander");
qmlRegisterType<QSGFrictionAffector>(uri, 2, 0, "Friction");
qmlRegisterType<QSGPointAttractorAffector>(uri, 2, 0, "PointAttractor");
@@ -104,8 +107,8 @@ void QSGParticlesModule::defineModule()
qmlRegisterType<QSGKillAffector>(uri, 2, 0, "Kill");
qmlRegisterType<QSGSpriteGoalAffector>(uri, 2, 0, "SpriteGoal");
qmlRegisterType<QSGTurbulenceAffector>(uri, 2, 0 , "Turbulence");
+ qmlRegisterType<QSGTargetAffector>(uri, 2, 0 , "Target");
}
QT_END_NAMESPACE
-//Q_EXPORT_PLUGIN2(Particles, QT_PREPEND_NAMESPACE(ParticlesModule))
diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp
index 2dc21299cf..d648d46330 100644
--- a/src/declarative/particles/qsgparticlesystem.cpp
+++ b/src/declarative/particles/qsgparticlesystem.cpp
@@ -44,19 +44,232 @@
#include "qsgparticleemitter_p.h"
#include "qsgparticleaffector_p.h"
#include "qsgparticlepainter_p.h"
+#include "qsgspriteengine_p.h"
+#include "qsgsprite_p.h"
+
+#include "qsgfollowemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
#include <cmath>
#include <QDebug>
QT_BEGIN_NAMESPACE
-QSGParticleData::QSGParticleData()
+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);
+}
+
+QSGParticleDataHeap::QSGParticleDataHeap()
+ : m_data(0)
+{
+ m_data.reserve(1000);
+ clear();
+}
+
+void QSGParticleDataHeap::grow() //###Consider automatic growth vs resize() calls from GroupData
+{
+ m_data.resize(1 << ++m_size);
+}
+
+void QSGParticleDataHeap::insert(QSGParticleData* data)
+{
+ int time = roundedTime(data->t + data->lifeSpan);
+ 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 QSGParticleDataHeap::top()
+{
+ if (m_end == 0)
+ return 1e24;
+ return m_data[0].time;
+}
+
+QSet<QSGParticleData*> QSGParticleDataHeap::pop()
+{
+ if (!m_end)
+ return QSet<QSGParticleData*> ();
+ QSet<QSGParticleData*> 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 QSGParticleDataHeap::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 QSGParticleDataHeap::contains(QSGParticleData* d)
+{
+ for (int i=0; i<m_end; i++)
+ if (m_data[i].data.contains(d))
+ return true;
+ return false;
+}
+
+void QSGParticleDataHeap::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 QSGParticleDataHeap::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 QSGParticleDataHeap::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);
+ }
+}
+
+QSGParticleGroupData::QSGParticleGroupData(int id, QSGParticleSystem* sys):index(id),m_size(0),m_system(sys)
+{
+ initList();
+}
+
+QSGParticleGroupData::~QSGParticleGroupData()
+{
+ foreach (QSGParticleData* d, data)
+ delete d;
+}
+
+int QSGParticleGroupData::size()
+{
+ return m_size;
+}
+
+QString QSGParticleGroupData::name()//### Worth caching as well?
+{
+ return m_system->m_groupIds.key(index);
+}
+
+void QSGParticleGroupData::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 QSGParticleData(m_system);
+ data[i]->group = index;
+ data[i]->index = i;
+ reusableIndexes << i;
+ }
+ int delta = newSize - m_size;
+ m_size = newSize;
+ foreach (QSGParticlePainter* p, painters)
+ p->setCount(p->count() + delta);
+}
+
+void QSGParticleGroupData::initList()
+{
+ dataHeap.clear();
+}
+
+void QSGParticleGroupData::kill(QSGParticleData* d){
+ Q_ASSERT(d->group == index);
+ d->lifeSpan = 0;//Kill off
+ foreach (QSGParticlePainter* p, painters)
+ p->reload(d);
+ reusableIndexes << d->index;
+}
+
+QSGParticleData* QSGParticleGroupData::newDatum(bool respectsLimits){
+ while (dataHeap.top() <= m_system->m_timeInt){
+ foreach (QSGParticleData* datum, dataHeap.pop()){
+ if (!datum->stillAlive()){
+ reusableIndexes << datum->index;
+ }else{
+ prepareRecycler(datum); //ttl has been altered mid-way, put it back
+ }
+ }
+ }
+
+ 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];
+}
+
+void QSGParticleGroupData::prepareRecycler(QSGParticleData* d){
+ dataHeap.insert(d);
+}
+
+QSGParticleData::QSGParticleData(QSGParticleSystem* sys)
: group(0)
, e(0)
+ , system(sys)
, index(0)
+ , systemIndex(-1)
{
x = 0;
y = 0;
t = -1;
+ lifeSpan = 0;
size = 0;
endSize = 0;
sx = 0;
@@ -70,9 +283,9 @@ QSGParticleData::QSGParticleData()
rotation = 0;
rotationSpeed = 0;
autoRotate = 0;
- animIdx = -1;
+ animIdx = 0;
frameDuration = 1;
- frameCount = 0;
+ frameCount = 1;
animT = -1;
color.r = 255;
color.g = 255;
@@ -83,12 +296,163 @@ QSGParticleData::QSGParticleData()
modelIndex = -1;
}
+void QSGParticleData::clone(const QSGParticleData& other)
+{
+ x = other.x;
+ y = other.y;
+ t = other.t;
+ lifeSpan = other.lifeSpan;
+ size = other.size;
+ endSize = other.endSize;
+ sx = other.sx;
+ sy = other.sy;
+ 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;
+ 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;
+}
+
+//sets the x accleration without affecting the instantaneous x velocity or position
+void QSGParticleData::setInstantaneousAX(qreal ax)
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ qreal sx = (this->sx + t*this->ax) - t*ax;
+ qreal ex = this->x + this->sx * t + 0.5 * this->ax * t * t;
+ qreal x = ex - t*sx - 0.5 * t*t*ax;
+
+ this->ax = ax;
+ this->sx = sx;
+ this->x = x;
+}
+
+//sets the x velocity without affecting the instantaneous x postion
+void QSGParticleData::setInstantaneousSX(qreal vx)
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ qreal sx = vx - t*this->ax;
+ qreal ex = this->x + this->sx * t + 0.5 * this->ax * t * t;
+ qreal x = ex - t*sx - 0.5 * t*t*this->ax;
+
+ this->sx = sx;
+ this->x = x;
+}
+
+//sets the instantaneous x postion
+void QSGParticleData::setInstantaneousX(qreal x)
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ this->x = x - t*this->sx - 0.5 * t*t*this->ax;
+}
+
+//sets the y accleration without affecting the instantaneous y velocity or position
+void QSGParticleData::setInstantaneousAY(qreal ay)
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ qreal sy = (this->sy + t*this->ay) - t*ay;
+ qreal ey = this->y + this->sy * t + 0.5 * this->ay * t * t;
+ qreal y = ey - t*sy - 0.5 * t*t*ay;
+
+ this->ay = ay;
+ this->sy = sy;
+ this->y = y;
+}
+
+//sets the y velocity without affecting the instantaneous y position
+void QSGParticleData::setInstantaneousSY(qreal vy)
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ qreal sy = vy - t*this->ay;
+ qreal ey = this->y + this->sy * t + 0.5 * this->ay * t * t;
+ qreal y = ey - t*sy - 0.5 * t*t*this->ay;
+
+ this->sy = sy;
+ this->y = y;
+}
+
+//sets the instantaneous Y position
+void QSGParticleData::setInstantaneousY(qreal y)
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ this->y = y - t*this->sy - 0.5 * t*t*this->ay;
+}
+
+qreal QSGParticleData::curX() const
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ return this->x + this->sx * t + 0.5 * this->ax * t * t;
+}
+
+qreal QSGParticleData::curSX() const
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ return this->sx + t*this->ax;
+}
+
+qreal QSGParticleData::curY() const
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ return y + sy * t + 0.5 * ay * t * t;
+}
+
+qreal QSGParticleData::curSY() const
+{
+ qreal t = (system->m_timeInt / 1000.0) - this->t;
+ return sy + t*ay;
+}
+
+void QSGParticleData::debugDump()
+{
+ qDebug() << "Particle" << systemIndex << group << "/" << index << stillAlive()
+ << "Pos: " << x << "," << y
+ //<< "Vel: " << sx << "," << sy
+ //<< "Acc: " << ax << "," << ay
+ << "Size: " << size << "," << endSize
+ << "Time: " << t << "," <<lifeSpan << ";" << (system->m_timeInt / 1000.0) ;
+}
+
+bool QSGParticleData::stillAlive()
+{
+ if (!system)
+ return false;
+ //fprintf(stderr, "%.9lf %.9lf\n",((qreal)system->m_timeInt/1000.0), (t+lifeSpan));
+ return (t + lifeSpan - EPSILON) > ((qreal)system->m_timeInt/1000.0);
+}
+
+float QSGParticleData::curSize()
+{
+ if (!system || !lifeSpan)
+ return 0.0f;
+ return size + (endSize - size) * (lifeLeft() / lifeSpan);
+}
+
+float QSGParticleData::lifeLeft()
+{
+ if (!system)
+ return 0.0f;
+ return (t + lifeSpan) - (system->m_timeInt/1000.0);
+}
+
QSGParticleSystem::QSGParticleSystem(QSGItem *parent) :
QSGItem(parent), m_particle_count(0), m_running(true)
- , m_startTime(0), m_overwrite(false)
- , m_componentComplete(false)
+ , m_startTime(0), m_nextIndex(0), m_componentComplete(false), m_spriteEngine(0)
{
- QSGParticleGroupData* gd = new QSGParticleGroupData;//Default group
+ QSGParticleGroupData* gd = new QSGParticleGroupData(0, this);//Default group
m_groupData.insert(0,gd);
m_groupIds.insert("",0);
m_nextGroupId = 1;
@@ -97,6 +461,17 @@ QSGParticleSystem::QSGParticleSystem(QSGItem *parent) :
this, SLOT(loadPainter(QObject*)));
}
+QSGParticleSystem::~QSGParticleSystem()
+{
+ foreach (QSGParticleGroupData* gd, m_groupData)
+ delete gd;
+}
+
+QDeclarativeListProperty<QSGSprite> QSGParticleSystem::particleStates()
+{
+ return QDeclarativeListProperty<QSGSprite>(this, &m_states, spriteAppend, spriteCount, spriteAt, spriteClear);
+}
+
void QSGParticleSystem::registerParticlePainter(QSGParticlePainter* p)
{
//TODO: a way to Unregister emitters, painters and affectors
@@ -125,20 +500,29 @@ void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a)
void QSGParticleSystem::loadPainter(QObject *p)
{
- if(!m_componentComplete)
+ if (!m_componentComplete)
return;
QSGParticlePainter* painter = qobject_cast<QSGParticlePainter*>(p);
Q_ASSERT(painter);//XXX
- foreach(QSGParticleGroupData* sg, m_groupData)
+ foreach (QSGParticleGroupData* sg, m_groupData)
sg->painters.remove(painter);
int particleCount = 0;
- if(painter->particles().isEmpty()){//Uses default particle
- particleCount += m_groupData[0]->size;
+ if (painter->particles().isEmpty()){//Uses default particle
+ QStringList def;
+ def << "";
+ painter->setParticles(def);
+ particleCount += m_groupData[0]->size();
m_groupData[0]->painters << painter;
}else{
- foreach(const QString &group, painter->particles()){
- particleCount += m_groupData[m_groupIds[group]]->size;
+ foreach (const QString &group, painter->particles()){
+ if (group != QLatin1String("") && !m_groupIds[group]){//new group
+ int id = m_nextGroupId++;
+ QSGParticleGroupData* gd = new QSGParticleGroupData(id, this);
+ m_groupIds.insert(group, id);
+ m_groupData.insert(id, gd);
+ }
+ particleCount += m_groupData[m_groupIds[group]]->size();
m_groupData[m_groupIds[group]]->painters << painter;
}
}
@@ -149,64 +533,50 @@ void QSGParticleSystem::loadPainter(QObject *p)
void QSGParticleSystem::emittersChanged()
{
- if(!m_componentComplete)
+ if (!m_componentComplete)
return;
m_emitters.removeAll(0);
- //Recalculate all counts, as emitter 'particle' may have changed as well
- //### Worth tracking previous 'particle' per emitter to do partial recalculations?
- m_particle_count = 0;
- int previousGroups = m_nextGroupId;
- QVector<int> previousSizes;
- previousSizes.resize(previousGroups);
- for(int i=0; i<previousGroups; i++)
- previousSizes[i] = m_groupData[i]->size;
- for(int i=0; i<previousGroups; i++)
- m_groupData[i]->size = 0;
+ QList<int> previousSizes;
+ QList<int> newSizes;
+ for (int i=0; i<m_nextGroupId; i++){
+ previousSizes << m_groupData[i]->size();
+ newSizes << 0;
+ }
- foreach(QSGParticleEmitter* e, m_emitters){//Populate groups and set sizes.
- if(!m_groupIds.contains(e->particle())
+ foreach (QSGParticleEmitter* e, m_emitters){//Populate groups and set sizes.
+ if (!m_groupIds.contains(e->particle())
|| (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier
- QSGParticleGroupData* gd = new QSGParticleGroupData;
int id = m_nextGroupId++;
+ QSGParticleGroupData* gd = new QSGParticleGroupData(id, this);
m_groupIds.insert(e->particle(), id);
m_groupData.insert(id, gd);
+ previousSizes << 0;
+ newSizes << 0;
}
- m_groupData[m_groupIds[e->particle()]]->size += e->particleCount();
- m_particle_count += e->particleCount();
+ newSizes[m_groupIds[e->particle()]] += e->particleCount();
//###: Cull emptied groups?
}
- foreach(QSGParticleGroupData* gd, m_groupData){//resize groups and update painters
- int id = m_groupData.key(gd);
-
- //TODO: Shrink back down! (but it has the problem of trying to remove the dead particles while maintaining integrity)
- gd->size = qMax(gd->size, id < previousGroups?previousSizes[id]:0);
-
- gd->data.resize(gd->size);
- if(id < previousGroups){
- for(int i=previousSizes[id]; i<gd->size; i++)
- gd->data[i] = 0;
- /*TODO:Consider salvaging partial updates, but have to batch changes to a single painter
- int delta = 0;
- delta = gd->size - previousSizes[id];
- foreach(QSGParticlePainter* painter, gd->painters){
- if(!painter->count() && delta){
- painter->reset();
- painter->update();
- }
- qDebug() << "Phi" << painter << painter->count() << delta;
- painter->setCount(painter->count() + delta);
- }
- */
- }
+ //TODO: Garbage collection?
+ m_particle_count = 0;
+ for (int i=0; i<m_nextGroupId; i++){
+ m_groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
+ m_particle_count += m_groupData[i]->size();
}
- foreach(QSGParticlePainter *p, m_particlePainters)
+
+ Q_ASSERT(m_particle_count >= m_bySysIdx.size());//XXX when GC done right
+ m_bySysIdx.resize(m_particle_count);
+
+ foreach (QSGParticlePainter *p, m_particlePainters)
loadPainter(p);
- if(m_particle_count > 16000)//###Investigate if these limits are worth warning about?
+ if (!m_states.isEmpty())
+ createEngine();
+
+ if (m_particle_count > 16000)//###Investigate if these limits are worth warning about?
qWarning() << "Particle system arbitarily believes it has a vast number of particles (>16000). Expect poor performance";
}
@@ -219,17 +589,51 @@ void QSGParticleSystem::setRunning(bool arg)
}
}
+void QSGParticleSystem::stateRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
+{
+ //Hooks up automatic state-associated stuff
+ QSGParticleSystem* sys = qobject_cast<QSGParticleSystem*>(prop->object->parent());
+ QSGSprite* sprite = qobject_cast<QSGSprite*>(prop->object);
+ if (!sprite || !sys)
+ return;
+ QStringList list;
+ list << sprite->name();
+ QSGParticleAffector* a = qobject_cast<QSGParticleAffector*>(value);
+ if (a){
+ a->setParentItem(sys);
+ a->setParticles(list);
+ a->setSystem(sys);
+ return;
+ }
+ QSGFollowEmitter* e = qobject_cast<QSGFollowEmitter*>(value);
+ if (e){
+ e->setParentItem(sys);
+ e->setFollow(sprite->name());
+ e->setSystem(sys);
+ return;
+ }
+ QSGParticlePainter* p = qobject_cast<QSGParticlePainter*>(value);
+ if (p){
+ p->setParentItem(sys);
+ p->setParticles(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 QSGParticleSystem::componentComplete()
+
{
QSGItem::componentComplete();
m_componentComplete = true;
- //if(!m_emitters.isEmpty() && !m_particlePainters.isEmpty())
+ //if (!m_emitters.isEmpty() && !m_particlePainters.isEmpty())
reset();
}
-void QSGParticleSystem::reset()//TODO: Needed?
+void QSGParticleSystem::reset()//TODO: Needed? Or just in component complete?
{
- if(!m_componentComplete)
+ if (!m_componentComplete)
return;
//Clear guarded pointers which have been deleted
@@ -237,54 +641,134 @@ void QSGParticleSystem::reset()//TODO: Needed?
cleared += m_emitters.removeAll(0);
cleared += m_particlePainters.removeAll(0);
cleared += m_affectors.removeAll(0);
- //qDebug() << "Reset" << m_emitters.count() << m_particles.count() << "Cleared" << cleared;
emittersChanged();
//TODO: Reset data
-// foreach(QSGParticlePainter* p, m_particlePainters)
+// foreach (QSGParticlePainter* p, m_particlePainters)
// p->reset();
-// foreach(QSGParticleEmitter* e, m_emitters)
+// foreach (QSGParticleEmitter* e, m_emitters)
// e->reset();
//### Do affectors need reset too?
- if(!m_running)
+ if (!m_running)
return;
- foreach(QSGParticlePainter *p, m_particlePainters){
+ foreach (QSGParticlePainter *p, m_particlePainters){
loadPainter(p);
p->reset();
}
- m_timestamp.start();//TODO: Better placement
+ m_timeInt = 0;
+ m_timestamp.restart();//TODO: Better placement
m_initialized = true;
}
-QSGParticleData* QSGParticleSystem::newDatum(int groupId)
+void QSGParticleSystem::createEngine()
{
+ if (!m_componentComplete)
+ return;
+ //### Solve the losses if size/states go down
+ foreach (QSGSprite* sprite, m_states){
+ bool exists = false;
+ foreach (const QString &name, m_groupIds.keys())
+ if (sprite->name() == name)
+ exists = true;
+ if (!exists){
+ int id = m_nextGroupId++;
+ QSGParticleGroupData* gd = new QSGParticleGroupData(id, this);
+ m_groupIds.insert(sprite->name(), id);
+ m_groupData.insert(id, gd);
+ }
+ }
+ if (m_states.count()){
+ //Reorder Sprite List so as to have the same order as groups
+ QList<QSGSprite*> newList;
+ for (int i=0; i<m_nextGroupId; i++){
+ bool exists = false;
+ QString name = m_groupData[i]->name();
+ foreach (QSGSprite* existing, m_states){
+ if (existing->name() == name){
+ newList << existing;
+ exists = true;
+ }
+ }
+ if (!exists){
+ newList << new QSGSprite(this);
+ newList.back()->setName(name);
+ }
+ }
+ m_states = newList;
+
+ if (!m_spriteEngine)
+ m_spriteEngine = new QSGSpriteEngine(this);
+ m_spriteEngine->setCount(m_particle_count);
+ m_spriteEngine->m_states = m_states;
+
+ connect(m_spriteEngine, SIGNAL(stateChanged(int)),
+ this, SLOT(particleStateChange(int)));
+
+ }else{
+ if (m_spriteEngine)
+ delete m_spriteEngine;
+ m_spriteEngine = 0;
+ }
+
+}
+
+void QSGParticleSystem::particleStateChange(int idx)
+{
+ moveGroups(m_bySysIdx[idx], m_spriteEngine->spriteState(idx));
+}
+
+void QSGParticleSystem::moveGroups(QSGParticleData *d, int newGIdx)
+{
+ QSGParticleData* pd = newDatum(newGIdx, false, d->systemIndex);
+ pd->clone(*d);
+ finishNewDatum(pd);
+
+ d->systemIndex = -1;
+ m_groupData[d->group]->kill(d);
+}
+
+int QSGParticleSystem::nextSystemIndex()
+{
+ if (!m_reusableIndexes.isEmpty()){
+ int ret = *(m_reusableIndexes.begin());
+ m_reusableIndexes.remove(ret);
+ return ret;
+ }
+ if (m_nextIndex >= m_bySysIdx.size())
+ m_bySysIdx.resize(m_bySysIdx.size() < 10 ? 10 : m_bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
+ return m_nextIndex++;
+}
+
+QSGParticleData* QSGParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex)
+{
Q_ASSERT(groupId < m_groupData.count());//XXX shouldn't really be an assert
- Q_ASSERT(m_groupData[groupId]->size);
-
- if( m_groupData[groupId]->nextIdx >= m_groupData[groupId]->size)
- m_groupData[groupId]->nextIdx = 0;
- int nextIdx = m_groupData[groupId]->nextIdx++;
-
- Q_ASSERT(nextIdx < m_groupData[groupId]->size);
- QSGParticleData* ret;
- if(m_groupData[groupId]->data[nextIdx]){//Recycle, it's faster.
- ret = m_groupData[groupId]->data[nextIdx];
- if(!m_overwrite && ret->stillAlive()){
- return 0;//Artificial longevity (or too fast emission) means this guy hasn't died. To maintain count, don't emit a new one
- }//###Reset?
+
+ QSGParticleData* ret = m_groupData[groupId]->newDatum(respectLimits);
+ if (!ret){
+ return 0;
+ }
+ if (sysIndex == -1){
+ if (ret->systemIndex == -1)
+ ret->systemIndex = nextSystemIndex();
}else{
- ret = new QSGParticleData;
- m_groupData[groupId]->data[nextIdx] = ret;
+ if (ret->systemIndex != -1){
+ if (m_spriteEngine)
+ m_spriteEngine->stopSprite(ret->systemIndex);
+ m_reusableIndexes << ret->systemIndex;
+ m_bySysIdx[ret->systemIndex] = 0;
+ }
+ ret->systemIndex = sysIndex;
}
+ m_bySysIdx[ret->systemIndex] = ret;
+
+ if (m_spriteEngine)
+ m_spriteEngine->startSprite(ret->systemIndex, ret->group);
- ret->system = this;
- ret->index = nextIdx;
- ret->group = groupId;
return ret;
}
@@ -292,20 +776,24 @@ void QSGParticleSystem::emitParticle(QSGParticleData* pd)
{// called from prepareNextFrame()->emitWindow - enforce?
//Account for relative emitter position
QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0));
- if(!offset.isNull()){
+ if (!offset.isNull()){
pd->x += offset.x();
pd->y += offset.y();
}
- foreach(QSGParticleAffector *a, m_affectors)
- if(a && a->m_needsReset)
- a->reset(pd);
- foreach(QSGParticlePainter* p, m_groupData[pd->group]->painters)
- if(p)
- p->load(pd);
+ finishNewDatum(pd);
}
+void QSGParticleSystem::finishNewDatum(QSGParticleData *pd){
+ m_groupData[pd->group]->prepareRecycler(pd);
+ foreach (QSGParticleAffector *a, m_affectors)
+ if (a && a->m_needsReset)
+ a->reset(pd);
+ foreach (QSGParticlePainter* p, m_groupData[pd->group]->painters)
+ if (p)
+ p->load(pd);
+}
qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p)
{
@@ -314,7 +802,7 @@ qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p)
if (!m_initialized)
return 0;//error in initialization
- if(m_syncList.isEmpty() || m_syncList.contains(p)){//Need to advance the simulation
+ if (m_syncList.isEmpty() || m_syncList.contains(p)){//Need to advance the simulation
m_syncList.clear();
//### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time.
@@ -323,125 +811,23 @@ qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p)
qreal time = m_timeInt / 1000.;
dt = time - dt;
m_needsReset.clear();
- foreach(QSGParticleEmitter* emitter, m_emitters)
- if(emitter)
+ if (m_spriteEngine)
+ m_spriteEngine->updateSprites(m_timeInt);
+
+ foreach (QSGParticleEmitter* emitter, m_emitters)
+ if (emitter)
emitter->emitWindow(m_timeInt);
- foreach(QSGParticleAffector* a, m_affectors)
- if(a)
+ foreach (QSGParticleAffector* a, m_affectors)
+ if (a)
a->affectSystem(dt);
- foreach(QSGParticleData* d, m_needsReset)
- foreach(QSGParticlePainter* p, m_groupData[d->group]->painters)
- if(p && d)
+ foreach (QSGParticleData* d, m_needsReset)
+ foreach (QSGParticlePainter* p, m_groupData[d->group]->painters)
+ if (p && d)
p->reload(d);
}
m_syncList << p;
return m_timeInt;
}
-//sets the x accleration without affecting the instantaneous x velocity or position
-void QSGParticleData::setInstantaneousAX(qreal ax)
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- qreal sx = (this->sx + t*this->ax) - t*ax;
- qreal ex = this->x + this->sx * t + 0.5 * this->ax * t * t;
- qreal x = ex - t*sx - 0.5 * t*t*ax;
-
- this->ax = ax;
- this->sx = sx;
- this->x = x;
-}
-
-//sets the x velocity without affecting the instantaneous x postion
-void QSGParticleData::setInstantaneousSX(qreal vx)
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- qreal sx = vx - t*this->ax;
- qreal ex = this->x + this->sx * t + 0.5 * this->ax * t * t;
- qreal x = ex - t*sx - 0.5 * t*t*this->ax;
-
- this->sx = sx;
- this->x = x;
-}
-
-//sets the instantaneous x postion
-void QSGParticleData::setInstantaneousX(qreal x)
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- this->x = x - t*this->sx - 0.5 * t*t*this->ax;
-}
-
-//sets the y accleration without affecting the instantaneous y velocity or position
-void QSGParticleData::setInstantaneousAY(qreal ay)
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- qreal sy = (this->sy + t*this->ay) - t*ay;
- qreal ey = this->y + this->sy * t + 0.5 * this->ay * t * t;
- qreal y = ey - t*sy - 0.5 * t*t*ay;
-
- this->ay = ay;
- this->sy = sy;
- this->y = y;
-}
-
-//sets the y velocity without affecting the instantaneous y position
-void QSGParticleData::setInstantaneousSY(qreal vy)
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- //qDebug() << t << (system->m_timeInt/1000.0) << this->x << this->sx << this->ax << this->x + this->sx * t + 0.5 * this->ax * t * t;
- qreal sy = vy - t*this->ay;
- qreal ey = this->y + this->sy * t + 0.5 * this->ay * t * t;
- qreal y = ey - t*sy - 0.5 * t*t*this->ay;
-
- this->sy = sy;
- this->y = y;
-}
-
-//sets the instantaneous Y position
-void QSGParticleData::setInstantaneousY(qreal y)
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- this->y = y - t*this->sy - 0.5 * t*t*this->ay;
-}
-
-qreal QSGParticleData::curX() const
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- return this->x + this->sx * t + 0.5 * this->ax * t * t;
-}
-
-qreal QSGParticleData::curSX() const
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- return this->sx + t*this->ax;
-}
-
-qreal QSGParticleData::curY() const
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- return y + sy * t + 0.5 * ay * t * t;
-}
-
-qreal QSGParticleData::curSY() const
-{
- qreal t = (system->m_timeInt / 1000.0) - this->t;
- return sy + t*ay;
-}
-
-void QSGParticleData::debugDump()
-{
- qDebug() << "Particle" << group
- << "Pos: " << x << "," << y
- << "Vel: " << sx << "," << sy
- << "Acc: " << ax << "," << ay
- << "Size: " << size << "," << endSize
- << "Time: " << t << "," <<lifeSpan;
-}
-
-bool QSGParticleData::stillAlive()
-{
- if(!system)
- return false;
- return (t + lifeSpan) > (system->m_timeInt/1000.0);
-}
QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h
index 45b4e164f1..3a902c67d1 100644
--- a/src/declarative/particles/qsgparticlesystem_p.h
+++ b/src/declarative/particles/qsgparticlesystem_p.h
@@ -48,6 +48,7 @@
#include <QHash>
#include <QPointer>
#include <QSignalMapper>
+#include <QtDeclarative/private/qsgsprite_p.h>
QT_BEGIN_HEADER
@@ -55,122 +56,75 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Declarative)
-
+class QSGParticleSystem;
class QSGParticleAffector;
class QSGParticleEmitter;
class QSGParticlePainter;
class QSGParticleData;
+class QSGSpriteEngine;
+class QSGSprite;
-
-class QSGParticleGroupData{
-public:
- QSGParticleGroupData():size(0),nextIdx(0)
- {}
- int size;
- int nextIdx;
- QSet<QSGParticlePainter*> painters;
- QVector<QSGParticleData*> data;
+struct QSGParticleDataHeapNode{
+ int time;//in ms
+ QSet<QSGParticleData*> data;//Set ptrs instead?
};
-class QSGParticleSystem : public QSGItem
-{
- Q_OBJECT
- Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
- Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
- Q_PROPERTY(bool overwrite READ overwrite WRITE setOverwrite NOTIFY overwriteChanged)//XXX: Should just be an implementation detail, but I can't decide which way
- /* The problem is that it ought to be false (usually) for stasis effects like model particles,
- but it ought to be true (usually) for burst effects where you want it to burst, and forget the old stuff
- Ideally burst never overflows? But that leads to crappy behaviour from crappy users...
- */
-
+class QSGParticleDataHeap {
+ //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:
- explicit QSGParticleSystem(QSGItem *parent = 0);
-
-bool isRunning() const
-{
- return m_running;
-}
-
-int startTime() const
-{
- return m_startTime;
-}
+ QSGParticleDataHeap();
+ void insert(QSGParticleData* data);
-int count(){ return m_particle_count; }
+ int top();
-signals:
+ QSet<QSGParticleData*> pop();
-void systemInitialized();
-void runningChanged(bool arg);
+ void clear();
-void startTimeChanged(int arg);
-
-
-void overwriteChanged(bool arg);
+ bool contains(QSGParticleData*);//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;
+ QSGParticleDataHeapNode m_tmp;
+ QVector<QSGParticleDataHeapNode> m_data;
+ QHash<int,int> m_lookups;
+};
-public slots:
-void reset();
-void setRunning(bool arg);
+class QSGParticleGroupData{
+public:
+ QSGParticleGroupData(int id, QSGParticleSystem* sys);
+ ~QSGParticleGroupData();
+ int size();
+ QString name();
-void setStartTime(int arg)
-{
- m_startTime = arg;
-}
+ void setSize(int newSize);
-void setOverwrite(bool arg)
-{
- if (m_overwrite != arg) {
- m_overwrite = arg;
-emit overwriteChanged(arg);
-}
-}
+ int index;
+ QSet<QSGParticlePainter*> painters;
-void fastForward(int ms)
-{
- m_startTime += ms;
-}
+ //TODO: Refactor particle data list out into a separate class
+ QVector<QSGParticleData*> data;
+ QSGParticleDataHeap dataHeap;
+ QSet<int> reusableIndexes;
-protected:
- void componentComplete();
+ void initList();
+ void kill(QSGParticleData* d);
-private slots:
- void emittersChanged();
- void loadPainter(QObject* p);
-public://but only really for related class usage. Perhaps we should all be friends?
- void emitParticle(QSGParticleData* p);
- QSGParticleData* newDatum(int groupId);
- qint64 systemSync(QSGParticlePainter* p);
- QElapsedTimer m_timestamp;
- QSet<QSGParticleData*> m_needsReset;
- QHash<QString, int> m_groupIds;
- QHash<int, QSGParticleGroupData*> m_groupData;
- qint64 m_timeInt;
- bool m_initialized;
+ //After calling this, initialize, then call prepareRecycler(d)
+ QSGParticleData* newDatum(bool respectsLimits);
- void registerParticlePainter(QSGParticlePainter* p);
- void registerParticleEmitter(QSGParticleEmitter* e);
- void registerParticleAffector(QSGParticleAffector* a);
- bool overwrite() const
- {
- return m_overwrite;
- }
+ //TODO: Find and clean up those that don't get added to the recycler (currently they get lost)
+ void prepareRecycler(QSGParticleData* d);
- int m_particle_count;
private:
- void initializeSystem();
- bool m_running;
- QList<QPointer<QSGParticleEmitter> > m_emitters;
- QList<QPointer<QSGParticleAffector> > m_affectors;
- QList<QPointer<QSGParticlePainter> > m_particlePainters;
- QList<QPointer<QSGParticlePainter> > m_syncList;
- qint64 m_startTime;
- int m_nextGroupId;
- bool m_overwrite;
- bool m_componentComplete;
-
- QSignalMapper m_painterMapper;
- QSignalMapper m_emitterMapper;
+ int m_size;
+ QSGParticleSystem* m_system;
};
struct Color4ub {
@@ -183,7 +137,7 @@ struct Color4ub {
class QSGParticleData{
public:
//TODO: QObject like memory management (without the cost, just attached to system)
- QSGParticleData();
+ QSGParticleData(QSGParticleSystem* sys);
//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.
@@ -211,6 +165,7 @@ public:
QSGParticleEmitter* e;//### Needed?
QSGParticleSystem* system;
int index;
+ int systemIndex;
//General Position Stuff
float x;
@@ -243,8 +198,114 @@ public:
void debugDump();
bool stillAlive();
+ float lifeLeft();
+ float curSize();
+ void clone(const QSGParticleData& other);//Not =, leaves meta-data like index
};
+class QSGParticleSystem : public QSGItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
+ Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
+
+ Q_PROPERTY(QDeclarativeListProperty<QSGSprite> particleStates READ particleStates)
+
+public:
+ explicit QSGParticleSystem(QSGItem *parent = 0);
+ ~QSGParticleSystem();
+ QDeclarativeListProperty<QSGSprite> particleStates();
+
+bool isRunning() const
+{
+ return m_running;
+}
+
+int startTime() const
+{
+ return m_startTime;
+}
+
+int count(){ return m_particle_count; }
+
+signals:
+
+void systemInitialized();
+void runningChanged(bool arg);
+
+void startTimeChanged(int arg);
+
+
+public slots:
+void reset();
+void setRunning(bool arg);
+
+
+void setStartTime(int arg)
+{
+ m_startTime = arg;
+}
+
+void fastForward(int ms)
+{
+ m_startTime += ms;
+}
+
+protected:
+ void componentComplete();
+
+private slots:
+ void emittersChanged();
+ void loadPainter(QObject* p);
+ void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
+ void particleStateChange(int idx);
+
+public://###but only really for related class usage. Perhaps we should all be friends?
+ //These can be called multiple times per frame, performance critical
+ void emitParticle(QSGParticleData* p);
+ QSGParticleData* newDatum(int groupId, bool respectLimits = true, int sysIdx = -1);//TODO: implement respectLimits in emitters (which means interacting with maxCount?)
+ void finishNewDatum(QSGParticleData*);
+ void moveGroups(QSGParticleData *d, int newGIdx);
+ int nextSystemIndex();
+
+ //This one only once per frame (effectively)
+ qint64 systemSync(QSGParticlePainter* p);
+
+ QElapsedTimer m_timestamp;
+ QSet<QSGParticleData*> m_needsReset;
+ QVector<QSGParticleData*> m_bySysIdx; //Another reference to the data (data owned by group), but by sysIdx
+ QHash<QString, int> m_groupIds;
+ QHash<int, QSGParticleGroupData*> m_groupData;
+ QSGSpriteEngine* m_spriteEngine;
+
+ qint64 m_timeInt;
+ bool m_initialized;
+
+ void registerParticlePainter(QSGParticlePainter* p);
+ void registerParticleEmitter(QSGParticleEmitter* e);
+ void registerParticleAffector(QSGParticleAffector* a);
+
+ int m_particle_count;
+ static void stateRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value);//From QSGSprite
+private:
+ void initializeSystem();
+ bool m_running;
+ QList<QPointer<QSGParticleEmitter> > m_emitters;
+ QList<QPointer<QSGParticleAffector> > m_affectors;
+ QList<QPointer<QSGParticlePainter> > m_particlePainters;
+ QList<QPointer<QSGParticlePainter> > m_syncList;
+ qint64 m_startTime;
+ int m_nextGroupId;
+ int m_nextIndex;
+ QSet<int> m_reusableIndexes;
+ bool m_componentComplete;
+ QList<QSGSprite*> m_states;
+
+ QSignalMapper m_painterMapper;
+ QSignalMapper m_emitterMapper;
+};
+
+
QT_END_NAMESPACE
QT_END_HEADER
diff --git a/src/declarative/particles/qsgpointattractor.cpp b/src/declarative/particles/qsgpointattractor.cpp
index 6f57ecaa36..8f3b06f5f7 100644
--- a/src/declarative/particles/qsgpointattractor.cpp
+++ b/src/declarative/particles/qsgpointattractor.cpp
@@ -51,14 +51,14 @@ QSGPointAttractorAffector::QSGPointAttractorAffector(QSGItem *parent) :
bool QSGPointAttractorAffector::affectParticle(QSGParticleData *d, qreal dt)
{
- if(m_strength == 0.0)
+ if (m_strength == 0.0)
return false;
qreal dx = m_y - d->curX();
qreal dy = m_x - d->curY();
qreal r = sqrt((dx*dx) + (dy*dy));
qreal theta = atan2(dy,dx);
qreal ds = 0;
- switch(m_proportionalToDistance){
+ switch (m_proportionalToDistance){
case Quadratic:
ds = (m_strength / qMax<qreal>(1.,r*r)) * dt;
break;
@@ -68,7 +68,7 @@ bool QSGPointAttractorAffector::affectParticle(QSGParticleData *d, qreal dt)
}
dx = ds * cos(theta);
dy = ds * sin(theta);
- switch(m_physics){
+ switch (m_physics){
case Position:
d->x = (d->x + dx);
d->y = (d->y + dy);
diff --git a/src/declarative/particles/qsgspritegoal.cpp b/src/declarative/particles/qsgspritegoal.cpp
index c97bfd1f26..6190f39c1d 100644
--- a/src/declarative/particles/qsgspritegoal.cpp
+++ b/src/declarative/particles/qsgspritegoal.cpp
@@ -48,20 +48,24 @@
QT_BEGIN_NAMESPACE
QSGSpriteGoalAffector::QSGSpriteGoalAffector(QSGItem *parent) :
- QSGParticleAffector(parent), m_goalIdx(-1), m_jump(false)
+ QSGParticleAffector(parent), m_goalIdx(-1), m_jump(false), m_systemStates(false), m_lastEngine(0), m_notUsingEngine(false)
{
}
void QSGSpriteGoalAffector::updateStateIndex(QSGSpriteEngine* e)
{
- m_lastEngine = e;
- for(int i=0; i<e->stateCount(); i++){
- if(e->state(i)->name() == m_goalState){
- m_goalIdx = i;
- return;
+ if (m_systemStates){
+ m_goalIdx = m_system->m_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
}
- m_goalIdx = -1;//Can't find it
}
void QSGSpriteGoalAffector::setGoalState(QString arg)
@@ -69,7 +73,7 @@ void QSGSpriteGoalAffector::setGoalState(QString arg)
if (m_goalState != arg) {
m_goalState = arg;
emit goalStateChanged(arg);
- if(m_goalState.isEmpty())
+ if (m_goalState.isEmpty())
m_goalIdx = -1;
else
m_goalIdx = -2;
@@ -79,18 +83,30 @@ void QSGSpriteGoalAffector::setGoalState(QString arg)
bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt)
{
Q_UNUSED(dt);
- //TODO: Affect all engines
QSGSpriteEngine *engine = 0;
- foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->painters)
- if(qobject_cast<QSGImageParticle*>(p))
- engine = qobject_cast<QSGImageParticle*>(p)->spriteEngine();
- if(!engine)
+ if (!m_systemStates){
+ //TODO: Affect all engines
+ foreach (QSGParticlePainter *p, m_system->m_groupData[d->group]->painters)
+ if (qobject_cast<QSGImageParticle*>(p))
+ engine = qobject_cast<QSGImageParticle*>(p)->spriteEngine();
+ }else{
+ engine = m_system->m_spriteEngine;
+ if (!engine)
+ m_notUsingEngine = true;
+ }
+ if (!engine && !m_notUsingEngine)
return false;
- if(m_goalIdx == -2 || engine != m_lastEngine)
+ if (m_goalIdx == -2 || engine != m_lastEngine)
updateStateIndex(engine);
- if(engine->spriteState(d->index) != m_goalIdx){
- engine->setGoal(m_goalIdx, d->index, m_jump);
+ 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->spriteState(index) != m_goalIdx){
+ engine->setGoal(m_goalIdx, index, m_jump);
emit affected(QPointF(d->curX(), d->curY()));//###Expensive if unconnected? Move to Affector?
return true; //Doesn't affect particle data, but necessary for onceOff
}
diff --git a/src/declarative/particles/qsgspritegoal_p.h b/src/declarative/particles/qsgspritegoal_p.h
index 28fb2939e6..720f2b42da 100644
--- a/src/declarative/particles/qsgspritegoal_p.h
+++ b/src/declarative/particles/qsgspritegoal_p.h
@@ -56,6 +56,7 @@ class QSGSpriteGoalAffector : public QSGParticleAffector
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 QSGSpriteGoalAffector(QSGItem *parent = 0);
@@ -68,6 +69,11 @@ public:
{
return m_jump;
}
+ bool systemStates() const
+ {
+ return m_systemStates;
+ }
+
protected:
virtual bool affectParticle(QSGParticleData *d, qreal dt);
signals:
@@ -77,6 +83,8 @@ signals:
void jumpChanged(bool arg);
void affected(const QPointF &pos);
+ void systemStatesChanged(bool arg);
+
public slots:
void setGoalState(QString arg);
@@ -89,12 +97,23 @@ void setJump(bool arg)
}
}
+void setSystemStates(bool arg)
+{
+ if (m_systemStates != arg) {
+ m_systemStates = arg;
+ emit systemStatesChanged(arg);
+ }
+}
+
private:
void updateStateIndex(QSGSpriteEngine* e);
QString m_goalState;
int m_goalIdx;
QSGSpriteEngine* m_lastEngine;
bool m_jump;
+ bool m_systemStates;
+
+ bool m_notUsingEngine;
};
QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgtargetaffector.cpp b/src/declarative/particles/qsgtargetaffector.cpp
new file mode 100644
index 0000000000..bf7d4d8eb5
--- /dev/null
+++ b/src/declarative/particles/qsgtargetaffector.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgtargetaffector_p.h"
+#include <QDebug>
+
+QSGTargetAffector::QSGTargetAffector(QSGItem *parent) :
+ QSGParticleAffector(parent), m_targetX(0), m_targetY(0),
+ m_targetWidth(0), m_targetHeight(0), m_defaultShape(new QSGParticleExtruder(this)),
+ m_targetShape(m_defaultShape), m_targetTime(-1)
+{
+ m_needsReset = true;
+}
+
+void QSGTargetAffector::reset(QSGParticleData* d)
+{
+ QSGParticleAffector::reset(d);
+ m_targets[qMakePair<int,int>(d->group, d->index)] = m_targetShape->extrude(QRectF(m_targetX, m_targetY, m_targetWidth, m_targetHeight));
+}
+
+bool QSGTargetAffector::affectParticle(QSGParticleData *d, qreal dt)
+{
+ Q_UNUSED(dt);
+ QPointF target = m_targets[qMakePair<int,int>(d->group, d->index)];
+ if (target.isNull())
+ return false;
+ qreal tt = m_targetTime==-1?d->lifeSpan:(m_targetTime / 1000.0);
+ qreal t = tt - (d->lifeSpan - d->lifeLeft());
+ if (t <= 0)
+ return false;
+ qreal tx = d->x + d->sx * tt + 0.5 * d->ax * tt * tt;
+ qreal ty = d->y + d->sy * tt + 0.5 * d->ay * tt * tt;
+
+ if (QPointF(tx,ty) == target)
+ return false;
+
+ qreal vX = (target.x() - d->x) / tt;
+ qreal vY = (target.y() - d->y) / tt;
+
+ qreal w = 1 - (t / tt) + 0.05;
+ w = qMin(w, 1.0);
+ qreal wvX = vX * w + d->sx * (1 - w);
+ qreal wvY = vY * w + d->sy * (1 - w);
+ //Screws with the acceleration so that the given start pos with the chosen weighted velocity will still end at the target coordinates
+ qreal ax = (2*(target.x() - d->x - wvX*tt)) / (tt*tt);
+ qreal ay = (2*(target.y() - d->y - wvY*tt)) / (tt*tt);
+
+ d->sx = wvX;
+ d->sy = wvY;
+ d->ax = ax;
+ d->ay = ay;
+
+ return true;
+}
diff --git a/src/declarative/particles/qsgtargetaffector_p.h b/src/declarative/particles/qsgtargetaffector_p.h
new file mode 100644
index 0000000000..74100e4ad0
--- /dev/null
+++ b/src/declarative/particles/qsgtargetaffector_p.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTARGETAFFECTOR_H
+#define QSGTARGETAFFECTOR_H
+
+#include "qsgparticleaffector_p.h"
+
+class QSGTargetAffector : public QSGParticleAffector
+{
+ Q_OBJECT
+ Q_PROPERTY(int targetX READ targetX WRITE setTargetX NOTIFY targetXChanged)
+ Q_PROPERTY(int targetY READ targetY WRITE setTargetY NOTIFY targetYChanged)
+ Q_PROPERTY(int targetWidth READ targetWidth WRITE setTargetWidth NOTIFY targetWidthChanged)
+ Q_PROPERTY(int targetHeight READ targetHeight WRITE setTargetHeight NOTIFY targetHeightChanged)
+ Q_PROPERTY(QSGParticleExtruder* targetShape READ targetShape WRITE setTargetShape NOTIFY targetShapeChanged)
+ Q_PROPERTY(int targetTime READ targetTime WRITE setTargetTime NOTIFY targetTimeChanged)
+
+public:
+ explicit QSGTargetAffector(QSGItem *parent = 0);
+
+ int targetX() const
+ {
+ return m_targetX;
+ }
+
+ int targetY() const
+ {
+ return m_targetY;
+ }
+
+ int targetWidth() const
+ {
+ return m_targetWidth;
+ }
+
+ int targetHeight() const
+ {
+ return m_targetHeight;
+ }
+
+ QSGParticleExtruder* targetShape() const
+ {
+ return m_targetShape;
+ }
+
+ int targetTime() const
+ {
+ return m_targetTime;
+ }
+
+signals:
+
+ void targetXChanged(int arg);
+
+ void targetYChanged(int arg);
+
+ void targetWidthChanged(int arg);
+
+ void targetHeightChanged(int arg);
+
+ void targetShapeChanged(QSGParticleExtruder* arg);
+
+ void targetTimeChanged(int arg);
+
+public slots:
+ void setTargetX(int arg)
+ {
+ if (m_targetX != arg) {
+ m_targetX = arg;
+ emit targetXChanged(arg);
+ }
+ }
+
+ void setTargetY(int arg)
+ {
+ if (m_targetY != arg) {
+ m_targetY = arg;
+ emit targetYChanged(arg);
+ }
+ }
+
+ void setTargetWidth(int arg)
+ {
+ if (m_targetWidth != arg) {
+ m_targetWidth = arg;
+ emit targetWidthChanged(arg);
+ }
+ }
+
+ void setTargetHeight(int arg)
+ {
+ if (m_targetHeight != arg) {
+ m_targetHeight = arg;
+ emit targetHeightChanged(arg);
+ }
+ }
+
+ void setTargetShape(QSGParticleExtruder* arg)
+ {
+ if (m_targetShape != arg) {
+ m_targetShape = arg;
+ emit targetShapeChanged(arg);
+ }
+ }
+
+ void setTargetTime(int arg)
+ {
+ if (m_targetTime != arg) {
+ m_targetTime = arg;
+ emit targetTimeChanged(arg);
+ }
+ }
+
+protected:
+ virtual void reset(QSGParticleData*);
+ virtual bool affectParticle(QSGParticleData *d, qreal dt);
+private:
+ int m_targetX;
+ int m_targetY;
+ int m_targetWidth;
+ int m_targetHeight;
+ QSGParticleExtruder* m_defaultShape;
+ QSGParticleExtruder* m_targetShape;
+ int m_targetTime;
+
+ QHash<QPair<int, int>, QPointF> m_targets;
+};
+
+#endif // QSGTARGETAFFECTOR_H
diff --git a/src/declarative/particles/qsgtargeteddirection.cpp b/src/declarative/particles/qsgtargeteddirection.cpp
index 9f1a868512..294242b938 100644
--- a/src/declarative/particles/qsgtargeteddirection.cpp
+++ b/src/declarative/particles/qsgtargeteddirection.cpp
@@ -62,11 +62,11 @@ const QPointF &QSGTargetedDirection::sample(const QPointF &from)
//###This approach loses interpolating the last position of the target (like we could with the emitter) is it worthwhile?
qreal targetX;
qreal targetY;
- if(m_targetItem){
+ if (m_targetItem){
QSGParticleEmitter* parentEmitter = qobject_cast<QSGParticleEmitter*>(parent());
targetX = m_targetItem->width()/2;
targetY = m_targetItem->height()/2;
- if(!parentEmitter){
+ 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();
@@ -83,7 +83,7 @@ const QPointF &QSGTargetedDirection::sample(const QPointF &from)
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)
+ if (m_proportionalMagnitude)
mag *= sqrt(targetX * targetX + targetY * targetY);
m_ret.setX(mag * cos(theta));
m_ret.setY(mag * sin(theta));
diff --git a/src/declarative/particles/qsgturbulence.cpp b/src/declarative/particles/qsgturbulence.cpp
index bb46b99f0d..941cf16c77 100644
--- a/src/declarative/particles/qsgturbulence.cpp
+++ b/src/declarative/particles/qsgturbulence.cpp
@@ -56,7 +56,7 @@ QSGTurbulenceAffector::QSGTurbulenceAffector(QSGItem *parent) :
QSGTurbulenceAffector::~QSGTurbulenceAffector()
{
if (m_field) {
- for(int i=0; i<m_gridSize; i++)
+ for (int i=0; i<m_gridSize; i++)
free(m_field[i]);
free(m_field);
}
@@ -70,8 +70,8 @@ static qreal magnitude(qreal x, qreal y)
void QSGTurbulenceAffector::setSize(int arg)
{
if (m_gridSize != arg) {
- if(m_field){ //deallocate and then reallocate grid
- for(int i=0; i<m_gridSize; i++)
+ 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;
@@ -83,14 +83,14 @@ void QSGTurbulenceAffector::setSize(int arg)
void QSGTurbulenceAffector::ensureInit()
{
- if(m_inited)
+ if (m_inited)
return;
m_inited = true;
m_field = (QPointF**)malloc(m_gridSize * sizeof(QPointF*));
- for(int i=0; i<m_gridSize; i++)
+ for (int i=0; i<m_gridSize; i++)
m_field[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF));
- for(int i=0; i<m_gridSize; i++)
- for(int j=0; j<m_gridSize; j++)
+ for (int i=0; i<m_gridSize; i++)
+ for (int j=0; j<m_gridSize; j++)
m_field[i][j] = QPointF();
m_spacing = QPointF(width()/m_gridSize, height()/m_gridSize);
m_magSum = magnitude(m_spacing.x(), m_spacing.y())*2;
@@ -101,11 +101,11 @@ void QSGTurbulenceAffector::mapUpdate()
QPoint pos(rand() % m_gridSize, rand() % m_gridSize);
QPointF vector(m_strength - (((qreal)rand() / RAND_MAX) * m_strength*2),
m_strength - (((qreal)rand() / RAND_MAX) * m_strength*2));
- for(int i = 0; i < m_gridSize; i++){
- for(int j = 0; j < m_gridSize; j++){
+ for (int i = 0; i < m_gridSize; i++){
+ for (int j = 0; j < m_gridSize; j++){
qreal dist = magnitude(i-pos.x(), j-pos.y());
m_field[i][j] += vector/(dist + 1);
- if(magnitude(m_field[i][j].x(), m_field[i][j].y()) > m_strength){
+ if (magnitude(m_field[i][j].x(), m_field[i][j].y()) > m_strength){
//Speed limit
qreal theta = atan2(m_field[i][j].y(), m_field[i][j].x());
m_field[i][j].setX(m_strength * cos(theta));
@@ -118,21 +118,21 @@ void QSGTurbulenceAffector::mapUpdate()
void QSGTurbulenceAffector::affectSystem(qreal dt)
{
- if(!m_system || !m_active)
+ if (!m_system || !m_active)
return;
ensureInit();
qreal period = 1.0/m_frequency;
qreal time = m_system->m_timeInt / 1000.0;
- while( m_lastT < time ){
+ while ( m_lastT < time ){
mapUpdate();
m_lastT += period;
}
- foreach(QSGParticleGroupData *gd, m_system->m_groupData){
- if(!activeGroup(m_system->m_groupData.key(gd)))//TODO: Surely this can be done better
+ foreach (QSGParticleGroupData *gd, m_system->m_groupData){
+ if (!activeGroup(m_system->m_groupData.key(gd)))//TODO: Surely this can be done better
return;
- foreach(QSGParticleData *d, gd->data){
- if(!d || !activeGroup(d->group))
+ foreach (QSGParticleData *d, gd->data){
+ if (!d || !activeGroup(d->group))
return;
qreal fx = 0.0;
qreal fy = 0.0;
@@ -144,14 +144,14 @@ void QSGTurbulenceAffector::affectSystem(qreal dt)
nodes << qMakePair((int)floor(nodePos.x()), (int)ceil(nodePos.y()));
nodes << qMakePair((int)floor(nodePos.x()), (int)floor(nodePos.y()));
typedef QPair<int, int> intPair;
- foreach(const intPair &p, nodes){
- if(!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second)))
+ foreach (const intPair &p, nodes){
+ if (!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second)))
continue;
qreal dist = magnitude(pos.x() - p.first*m_spacing.x(), pos.y() - p.second*m_spacing.y());//TODO: Mathematically valid
fx += m_field[p.first][p.second].x() * ((m_magSum - dist)/m_magSum);//Proportionally weight nodes
fy += m_field[p.first][p.second].y() * ((m_magSum - dist)/m_magSum);
}
- if(fx || fy){
+ if (fx || fy){
d->setInstantaneousSX(d->curSX()+ fx * dt);
d->setInstantaneousSY(d->curSY()+ fy * dt);
m_system->m_needsReset << d;
diff --git a/src/declarative/particles/qsgwander.cpp b/src/declarative/particles/qsgwander.cpp
index 26bea4ee04..c7e17c556b 100644
--- a/src/declarative/particles/qsgwander.cpp
+++ b/src/declarative/particles/qsgwander.cpp
@@ -52,14 +52,14 @@ QSGWanderAffector::QSGWanderAffector(QSGItem *parent) :
QSGWanderAffector::~QSGWanderAffector()
{
- for(QHash<int, WanderData*>::const_iterator iter=m_wanderData.constBegin();
+ for (QHash<int, WanderData*>::const_iterator iter=m_wanderData.constBegin();
iter != m_wanderData.constEnd(); iter++)
delete (*iter);
}
WanderData* QSGWanderAffector::getData(int idx)
{
- if(m_wanderData.contains(idx))
+ if (m_wanderData.contains(idx))
return m_wanderData[idx];
WanderData* d = new WanderData;
d->x_vel = 0;
@@ -75,7 +75,7 @@ WanderData* QSGWanderAffector::getData(int idx)
void QSGWanderAffector::reset(int systemIdx)
{
- if(m_wanderData.contains(systemIdx))
+ if (m_wanderData.contains(systemIdx))
delete m_wanderData[systemIdx];
m_wanderData.remove(systemIdx);
}
@@ -112,30 +112,30 @@ bool QSGWanderAffector::affectParticle(QSGParticleData* data, qreal dt)
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_physics){
+ switch (m_physics){
case Position:
newX = data->curX() + dx;
- if(m_xVariance > qAbs(newX) )
+ if (m_xVariance > qAbs(newX) )
data->x += dx;
newY = data->curY() + dy;
- if(m_yVariance > qAbs(newY) )
+ if (m_yVariance > qAbs(newY) )
data->y += dy;
break;
default:
case Velocity:
newX = data->curSX() + dx;
- if(m_xVariance > qAbs(newX) )
+ if (m_xVariance > qAbs(newX) )
data->setInstantaneousSX(newX);
newY = data->curSY() + dy;
- if(m_yVariance > qAbs(newY) )
+ if (m_yVariance > qAbs(newY) )
data->setInstantaneousSY(newY);
break;
case Acceleration:
newX = data->ax + dx;
- if(m_xVariance > qAbs(newX) )
+ if (m_xVariance > qAbs(newX) )
data->setInstantaneousAX(newX);
newY = data->ay + dy;
- if(m_yVariance > qAbs(newY) )
+ if (m_yVariance > qAbs(newY) )
data->setInstantaneousAY(newY);
break;
}