aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Alpert <alan.alpert@nokia.com>2011-07-04 18:15:28 +1000
committerQt by Nokia <qt-info@nokia.com>2011-07-06 10:09:09 +0200
commit29c4b643272a43022081cce063394bac823ab529 (patch)
tree10e4fe15bdf68dec90edac0d67b949a92d9204d5
parent87822d24df32311a50dc87ded55ad4d17e8226f0 (diff)
Squashed Particle System Stateful Rewrite
Add TargetAffector Fix for ParticlePainter offsets Adds a particleStates property to ParticleSystem Augment SpriteGoal to change system states as well Also add 'collidingParticles' list to affector. Particle Stochastic States Now actually working, and you can put emitters, affectors and painters inside their targeted state. Fireworks example uses states instead of delegates. Replaced the delegate example with a text thing. The examples launcher now also contains all the custom examples. Adds CumulativeDirection and working null Affector (for affected signal). Add spaces after all flow control keywords. Change-Id: I77b7e3044a9800dbfff6db833914d63127602cf5 Reviewed-on: http://codereview.qt.nokia.com/968 Reviewed-by: Alan Alpert <alan.alpert@nokia.com>
-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;
}