aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Alpert <alan.alpert@nokia.com>2011-06-15 18:52:26 +1000
committerAlan Alpert <alan.alpert@nokia.com>2011-06-15 18:52:26 +1000
commitfab0da3853efc5ef2f1a507f7d054b8a0dd3b9ac (patch)
tree56eeb5e2175c0abed7afb47821de0940ca7b2e52
parent7b22f02961a97c0fa07127ea3ff26b8435a617b8 (diff)
Rewrite particle system to cope with changing particle counts
And might do it again... Caught up in this massive change were the following smaller ones: -Some custom particle examples -delegate property on ItemParticle and an example with it
-rw-r--r--examples/declarative/particles/custom/fireworks.qml37
-rw-r--r--examples/declarative/particles/custom/shader.qml84
-rw-r--r--examples/declarative/particles/trails/dynamicemitters.qml8
-rw-r--r--src/declarative/particles/qsgcustomparticle.cpp143
-rw-r--r--src/declarative/particles/qsgcustomparticle_p.h10
-rw-r--r--src/declarative/particles/qsgfollowemitter.cpp12
-rw-r--r--src/declarative/particles/qsgimageparticle.cpp255
-rw-r--r--src/declarative/particles/qsgimageparticle_p.h13
-rw-r--r--src/declarative/particles/qsgitemparticle.cpp24
-rw-r--r--src/declarative/particles/qsgitemparticle_p.h4
-rw-r--r--src/declarative/particles/qsgmodelparticle.cpp18
-rw-r--r--src/declarative/particles/qsgmodelparticle_p.h4
-rw-r--r--src/declarative/particles/qsgparticleaffector.cpp36
-rw-r--r--src/declarative/particles/qsgparticleaffector_p.h6
-rw-r--r--src/declarative/particles/qsgparticlepainter.cpp46
-rw-r--r--src/declarative/particles/qsgparticlepainter_p.h45
-rw-r--r--src/declarative/particles/qsgparticlesystem.cpp235
-rw-r--r--src/declarative/particles/qsgparticlesystem_p.h28
-rw-r--r--src/declarative/particles/qsgspritegoal.cpp6
-rw-r--r--src/declarative/particles/qsgturbulence.cpp50
20 files changed, 655 insertions, 409 deletions
diff --git a/examples/declarative/particles/custom/fireworks.qml b/examples/declarative/particles/custom/fireworks.qml
index edd4accfdf..b73a5e234f 100644
--- a/examples/declarative/particles/custom/fireworks.qml
+++ b/examples/declarative/particles/custom/fireworks.qml
@@ -45,22 +45,6 @@ Rectangle{
width: 360
height: 600
color: "black"
- ParticleSystem{
- id: otherSys
- anchors.fill: parent
- Emitter{
- id: emitter
- emitting: false
- emitRate: 100
- lifeSpan: 1000
- emitCap: 1000
- speed: AngledDirection{angleVariation:180; magnitudeVariation: 60}
- }
-
- ImageParticle{
- source: "content/particle.png"
- }
- }
Component{
id: firework
Item{
@@ -79,14 +63,28 @@ Rectangle{
repeat: false
onTriggered: {
img.visible = false;
- emitter.burst(100, container.x+24, container.y+24);
+ 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
Emitter{
+ particle: "fire"
width: parent.width
y: parent.height
emitRate: 2
@@ -94,8 +92,13 @@ Rectangle{
speed: PointDirection{y:-100}
}
ItemParticle{
+ particles: ["fire"]
delegate: firework
}
+ ImageParticle{
+ particles: ["works"]
+ source: "content/particle.png"
+ }
}
}
diff --git a/examples/declarative/particles/custom/shader.qml b/examples/declarative/particles/custom/shader.qml
new file mode 100644
index 0000000000..d83e7864c4
--- /dev/null
+++ b/examples/declarative/particles/custom/shader.qml
@@ -0,0 +1,84 @@
+import QtQuick 2.0
+import QtQuick.Particles 2.0
+
+ParticleSystem{
+ id: root
+ width: 1024
+ height: 768
+ Rectangle{
+ z: -1
+ anchors.fill: parent
+ color: "black"
+ Text{
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ font.pixelSize: 36
+ color: "white"
+ text: "It's all in the fragment shader."
+ }
+ }
+ Emitter{
+ emitRate: 400
+ lifeSpan: 8000
+ size: 24
+ sizeVariation: 16
+ speed: PointDirection{x: root.width/10; y: root.height/10;}
+ //acceleration: AngledDirection{angle:225; magnitude: root.width/36; angleVariation: 45; magnitudeVariation: 80}
+ acceleration: PointDirection{x: -root.width/40; y: -root.height/40; xVariation: -root.width/20; yVariation: -root.width/20}
+ }
+ CustomParticle{
+ //TODO: Someway that you don't have to rewrite the basics for a simple addition
+ vertexShader:"
+ attribute highp vec2 vPos;
+ attribute highp vec2 vTex;
+ attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize
+ attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration
+ attribute highp float r;
+
+ uniform highp mat4 qt_ModelViewProjectionMatrix;
+ uniform highp float timestamp;
+ uniform lowp float qt_Opacity;
+
+ varying highp vec2 fTex;
+ varying lowp float fFade;
+ varying highp vec2 fPos;
+
+ void main() {
+ fTex = vTex;
+ highp float size = vData.z;
+ highp float endSize = vData.w;
+
+ highp float t = (timestamp - vData.x) / vData.y;
+
+ highp float currentSize = mix(size, endSize, t * t);
+
+ if (t < 0. || t > 1.)
+ currentSize = 0.;
+
+ highp vec2 pos = vPos
+ - currentSize / 2. + currentSize * vTex // adjust size
+ + vVec.xy * t * vData.y // apply speed vector..
+ + 0.5 * vVec.zw * pow(t * vData.y, 2.);
+
+ gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1);
+
+ highp float fadeIn = min(t * 20., 1.);
+ highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.));
+
+ fFade = fadeIn * fadeOut * qt_Opacity;
+ fPos = vec2(pos.x/1024., pos.y/768.);
+ }
+ "
+ fragmentShader: "
+ varying highp vec2 fPos;
+ varying lowp float fFade;
+ varying highp vec2 fTex;
+ void main() {//*2 because this generates dark colors mostly
+ highp vec2 circlePos = fTex*2.0 - vec2(1.0,1.0);
+ highp float dist = length(circlePos);
+ highp float circleFactor = max(min(1.0 - dist, 1.0), 0.0);
+ gl_FragColor = vec4(fPos.x*2.0 - fPos.y, fPos.y*2.0 - fPos.x, fPos.x*fPos.y*2.0, 0.0) * circleFactor * fFade;
+ }"
+
+ }
+}
diff --git a/examples/declarative/particles/trails/dynamicemitters.qml b/examples/declarative/particles/trails/dynamicemitters.qml
index 588474fb43..f338c204db 100644
--- a/examples/declarative/particles/trails/dynamicemitters.qml
+++ b/examples/declarative/particles/trails/dynamicemitters.qml
@@ -81,7 +81,7 @@ Rectangle{
}
system: sys
emitting: true
- emitRate: 64
+ emitRate: 32
lifeSpan: 600
size: 24
endSize: 8
@@ -107,12 +107,12 @@ Rectangle{
MouseArea{
anchors.fill: parent
onClicked:{
- for(var i=0; i<16; i++){
+ for(var i=0; i<8; i++){
var obj = emitterComp.createObject(root);
obj.x = mouse.x
obj.y = mouse.y
- obj.targetX = Math.random() * 640
- obj.targetY = Math.random() * 480
+ 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.go();
}
diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp
index 808ff1c427..f91307ed5c 100644
--- a/src/declarative/particles/qsgcustomparticle.cpp
+++ b/src/declarative/particles/qsgcustomparticle.cpp
@@ -44,16 +44,8 @@
#include <cstdlib>
QT_BEGIN_NAMESPACE
-/*
- "uniform highp mat4 qt_ModelViewProjectionMatrix; \n"
- "attribute highp vec4 qt_Vertex; \n"
- "attribute highp vec2 qt_MultiTexCoord0; \n"
- "varying highp vec2 qt_TexCoord0; \n"
- "void main() { \n"
- " qt_TexCoord0 = qt_MultiTexCoord0; \n"
- " gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; \n"
- "}";
-*/
+
+//TODO: Can we make the code such that you don't have to copy the whole vertex shader just to add one little calculation?
//Includes comments because the code isn't self explanatory
static const char qt_particles_default_vertex_code[] =
"attribute highp vec2 vPos; \n"
@@ -86,23 +78,6 @@ static const char qt_particles_default_fragment_code[] =//TODO: Default frag req
" gl_FragColor = texture2D(source, fTex) * qt_Opacity; \n"
"}";
-/*
-static const char qt_particles_default_vertex_code[] =
- "attribute highp vec2 vPos; \n"
- "attribute highp vec2 vTex; \n"
- "uniform highp mat4 qt_ModelViewProjectionMatrix; \n"
- "void main() { \n"
- " highp float currentSize = 1000.0; \n"
- " highp vec2 pos = vec2(100.0,100.0) \n"
- " - currentSize / 2. + currentSize * vTex; // adjust size \n"
- " gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1); \n"
- "}";
-static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source?
- "void main() { \n"
- " gl_FragColor = vec4(0,255,0,255); \n"
- "}";
-*/
-
static const char qt_position_attribute_name[] = "qt_Vertex";
static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
@@ -148,8 +123,36 @@ QSGCustomParticle::QSGCustomParticle(QSGItem* parent)
: QSGParticlePainter(parent)
, m_pleaseReset(true)
, m_dirtyData(true)
+ , m_resizePending(false)
{
setFlag(QSGItem::ItemHasContents);
+ m_defaultVertices = new PlainVertices;
+ PlainVertex* vertices = (PlainVertex*) m_defaultVertices;
+ for (int i=0; i<4; ++i) {
+ vertices[i].x = 0;
+ vertices[i].y = 0;
+ vertices[i].t = -1;
+ vertices[i].lifeSpan = 0;
+ vertices[i].size = 0;
+ vertices[i].endSize = 0;
+ vertices[i].sx = 0;
+ vertices[i].sy = 0;
+ vertices[i].ax = 0;
+ vertices[i].ay = 0;
+ vertices[i].r = 0;
+ }
+
+ 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;
}
void QSGCustomParticle::componentComplete()
@@ -199,10 +202,17 @@ void QSGCustomParticle::setVertexShader(const QByteArray &code)
emit vertexShaderChanged();
}
-void QSGCustomParticle::setCount(int c)
+void QSGCustomParticle::resize(int oldCount, int newCount)
{
- QSGParticlePainter::setCount(c);
- m_pleaseReset = true;
+ if(!m_node)
+ return;
+ if(!m_resizePending){
+ m_pendingResizeVector.resize(oldCount);
+ PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData();
+ for(int i=0; i<oldCount; i++)
+ m_pendingResizeVector[i] = &particles[i];
+ }
+ groupShuffle(m_pendingResizeVector, m_defaultVertices);
}
void QSGCustomParticle::reset()
@@ -401,6 +411,8 @@ QSGNode *QSGCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
m_pleaseReset = false;
m_dirtyData = false;
}
+ if(m_resizePending)
+ performPendingResize();
if(m_system && m_system->isRunning())
prepareNextFrame();
@@ -425,6 +437,32 @@ void QSGCustomParticle::prepareNextFrame(){
buildData();
}
+void QSGCustomParticle::performPendingResize()
+{
+ m_resizePending = false;
+ if(!m_node)
+ return;
+ Q_ASSERT(m_pendingResizeVector.size() == m_count);//XXX
+ PlainVertices tmp[m_count];//###More vast memcpys that will decrease performance
+ for(int i=0; i<m_count; i++)
+ tmp[i] = *m_pendingResizeVector[i];
+ m_node->setFlag(QSGNode::OwnsGeometry, false);
+ m_node->geometry()->allocate(m_count*4, m_count*6);
+ memcpy(m_node->geometry()->vertexData(), tmp, sizeof(PlainVertices) * m_count);
+ quint16 *indices = m_node->geometry()->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->setFlag(QSGNode::OwnsGeometry, true);
+}
+
QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
{
if (m_count * 4 > 0xffff) {
@@ -437,6 +475,7 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
return 0;
}
+ m_resizePending = false;//reset resizes as well.
//Create Particle Geometry
int vCount = m_count * 4;
@@ -446,32 +485,9 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
PlainVertex *vertices = (PlainVertex *) g->vertexData();
for (int p=0; p<m_count; ++p) {
double r = rand()/(double)RAND_MAX;//TODO: Seed?
- for (int i=0; i<4; ++i) {
- vertices[i].x = 0;
- vertices[i].y = 0;
- vertices[i].t = -1;
- vertices[i].lifeSpan = 0;
- vertices[i].size = 0;
- vertices[i].endSize = 0;
- vertices[i].sx = 0;
- vertices[i].sy = 0;
- vertices[i].ax = 0;
- vertices[i].ay = 0;
+ memcpy(vertices, m_defaultVertices, sizeof(PlainVertices));
+ for(int i=0; i<4; i++)
vertices[i].r = r;
- }
-
- 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();
@@ -493,23 +509,6 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
node->setGeometry(g);
node->setMaterial(&m_material);
- /*
- //For debugging, just use grid vertices like ShaderEffect
- node->setGeometry(0);
- QSGShaderEffectMesh* mesh = new QSGGridMesh();
- node->setFlag(QSGNode::OwnsGeometry, false);
-
- qDebug() << m_source.attributeNames;
- QSGGeometry* geometry = node->geometry();
- geometry = mesh->updateGeometry(geometry, m_source.attributeNames, QRectF(0,0,width(),height()));
- if(!geometry)
- qDebug() << "Should have written the error handling";
- else
- qDebug() << "Mesh Loaded";
- node->setGeometry(geometry);
- qDebug() << QString("INIT") << geometry << (QObject*)node;
- node->setFlag(QSGNode::OwnsGeometry, true);
- */
QSGShaderEffectProgram s = m_source;
if (s.fragmentCode.isEmpty())
s.fragmentCode = qt_particles_default_fragment_code;
diff --git a/src/declarative/particles/qsgcustomparticle_p.h b/src/declarative/particles/qsgcustomparticle_p.h
index 95144ef638..863da052f3 100644
--- a/src/declarative/particles/qsgcustomparticle_p.h
+++ b/src/declarative/particles/qsgcustomparticle_p.h
@@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Declarative)
class QSGNode;
-
+struct PlainVertices;
//Genealogy: Hybrid of UltraParticle and ShaderEffectItem
class QSGCustomParticle : public QSGParticlePainter
{
@@ -64,7 +64,6 @@ public:
explicit QSGCustomParticle(QSGItem* parent=0);
virtual void load(QSGParticleData*);
virtual void reload(QSGParticleData*);
- virtual void setCount(int c);
QByteArray fragmentShader() const { return m_source.fragmentCode; }
void setFragmentShader(const QByteArray &code);
@@ -84,14 +83,17 @@ protected:
void disconnectPropertySignals();
void connectPropertySignals();
void reset();
+ void resize(int oldCount, int newCount);
void updateProperties();
void lookThroughShaderCode(const QByteArray &code);
virtual void componentComplete();
QSGShaderEffectNode *buildCustomNode();
+ void performPendingResize();
private:
void buildData();
+
bool m_pleaseReset;
bool m_dirtyData;
QSGShaderEffectProgram m_source;
@@ -105,6 +107,10 @@ private:
QSGShaderEffectMaterial m_material;
QSGShaderEffectNode* m_node;
qreal m_lastTime;
+
+ bool m_resizePending;
+ QVector<PlainVertices*> m_pendingResizeVector;
+ PlainVertices* m_defaultVertices;
};
QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgfollowemitter.cpp b/src/declarative/particles/qsgfollowemitter.cpp
index 442cff9ec8..28a082f776 100644
--- a/src/declarative/particles/qsgfollowemitter.cpp
+++ b/src/declarative/particles/qsgfollowemitter.cpp
@@ -54,6 +54,7 @@ QSGFollowEmitter::QSGFollowEmitter(QSGItem *parent) :
, m_emissionExtruder(0)
, m_defaultEmissionExtruder(new QSGParticleExtruder(this))
{
+ //TODO: If followed increased their size
connect(this, SIGNAL(followChanged(QString)),
this, SLOT(recalcParticlesPerSecond()));
connect(this, SIGNAL(particleDurationChanged(int)),
@@ -67,7 +68,7 @@ void QSGFollowEmitter::recalcParticlesPerSecond(){
return;
m_followCount = m_system->m_groupData[m_system->m_groupIds[m_follow]]->size;
if(!m_followCount){
- setParticlesPerSecond(1000);//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)
+ 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);
@@ -111,16 +112,15 @@ void QSGFollowEmitter::emitWindow(int timeStamp)
int gId = m_system->m_groupIds[m_follow];
int gId2 = m_system->m_groupIds[m_particle];
- for(int i=0; i<m_system->m_groupData[gId]->size; i++){
- pt = m_lastEmission[i];
- QSGParticleData* d = m_system->m_data[i + m_system->m_groupData[gId]->start];
+ foreach(QSGParticleData *d, m_system->m_groupData[gId]->data){
if(!d || !d->stillAlive())
continue;
+ pt = m_lastEmission[d->index];
if(pt < d->pv.t)
pt = d->pv.t;
if(!effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){
- m_lastEmission[i] = time;//jump over this time period without emitting, because it's outside
+ m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside
continue;
}
while(pt < time || !m_burstQueue.isEmpty()){
@@ -187,7 +187,7 @@ void QSGFollowEmitter::emitWindow(int timeStamp)
pt += particleRatio;
}
}
- m_lastEmission[i] = pt;
+ m_lastEmission[d->index] = pt;
}
m_lastTimeStamp = time;
diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp
index c9df5f4dbd..15bc88b4c3 100644
--- a/src/declarative/particles/qsgimageparticle.cpp
+++ b/src/declarative/particles/qsgimageparticle.cpp
@@ -305,6 +305,62 @@ QSGImageParticle::QSGImageParticle(QSGItem* parent)
, m_lastLevel(Unknown)
{
setFlag(ItemHasContents);
+ //TODO: Clean up defaults here and in custom
+ m_defaultUltra = new UltraVertices;
+ m_defaultSimple = new SimpleVertices;
+ UltraVertex *vertices = (UltraVertex *) m_defaultUltra;
+ SimpleVertex *vertices2 = (SimpleVertex *) m_defaultSimple;
+ for (int i=0; i<4; ++i) {
+ vertices2[i].x = vertices[i].x = 0;
+ vertices2[i].y = vertices[i].y = 0;
+ vertices2[i].t = vertices[i].t = -1;
+ vertices2[i].lifeSpan = vertices[i].lifeSpan = 0;
+ vertices2[i].size = vertices[i].size = 0;
+ vertices2[i].endSize = vertices[i].endSize = 0;
+ vertices2[i].sx = vertices[i].sx = 0;
+ vertices2[i].sy = vertices[i].sy = 0;
+ vertices2[i].ax = vertices[i].ax = 0;
+ vertices2[i].ay = vertices[i].ay = 0;
+ vertices[i].xx = 1;
+ vertices[i].xy = 0;
+ vertices[i].yx = 0;
+ vertices[i].yy = 1;
+ vertices[i].rotation = 0;
+ vertices[i].rotationSpeed = 0;
+ vertices[i].autoRotate = 0;
+ vertices[i].animIdx = -1;
+ vertices[i].frameDuration = 1;
+ vertices[i].frameCount = 0;
+ vertices[i].animT = -1;
+ vertices[i].color.r = 255;
+ vertices[i].color.g = 255;
+ vertices[i].color.b = 255;
+ vertices[i].color.a = 255;
+ }
+
+ 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;
+
+ vertices2[0].tx = 0;
+ vertices2[0].ty = 0;
+
+ vertices2[1].tx = 1;
+ vertices2[1].ty = 0;
+
+ vertices2[2].tx = 0;
+ vertices2[2].ty = 1;
+
+ vertices2[3].tx = 1;
+ vertices2[3].ty = 1;
}
QDeclarativeListProperty<QSGSprite> QSGImageParticle::sprites()
@@ -498,11 +554,6 @@ void QSGImageParticle::setBloat(bool arg)
if(perfLevel < 9999)
reset();
}
-void QSGImageParticle::setCount(int c)
-{
- QSGParticlePainter::setCount(c);
- m_pleaseReset = true;
-}
void QSGImageParticle::reset()
{
@@ -568,35 +619,9 @@ QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode()
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 = 0;
- vertices[i].y = 0;
- vertices[i].t = -1;
- vertices[i].lifeSpan = 0;
- vertices[i].size = 0;
- vertices[i].endSize = 0;
- vertices[i].sx = 0;
- vertices[i].sy = 0;
- vertices[i].ax = 0;
- vertices[i].ay = 0;
- }
-
- 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;
- }
+ SimpleVertices *vertices = (SimpleVertices *) g->vertexData();
+ for (int p=0; p<m_count; ++p)
+ memcpy(vertices++, m_defaultSimple, sizeof(SimpleVertices));
quint16 *indices = g->indexDataAsUShort();
for (int i=0; i<m_count; ++i) {
@@ -640,6 +665,7 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
return 0;
}
+ m_resizePending = false;
if(!m_sprites.count() && !m_bloat
&& m_colortable_name.isEmpty()
&& m_sizetable_name.isEmpty()
@@ -685,8 +711,8 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
if(m_lastLevel == 1)
qDebug() << "Theta" << m_lastLevel << oldSimple[0].x << oldSimple[0].y << oldSimple[0].t;
for (int p=0; p<m_count; ++p) {
-
- if (m_lastLevel == 1) {//Transplant/IntermediateVertices?
+ memcpy(vertices, m_defaultUltra, sizeof(UltraVertices));
+ if (m_lastLevel == 1 && m_lastCount > p) {//Transplant/IntermediateVertices?
for (int i=0; i<4; ++i) {
vertices[i].x = oldSimple[i].x;
vertices[i].y = oldSimple[i].y;
@@ -698,64 +724,14 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
vertices[i].sy = oldSimple[i].sy;
vertices[i].ax = oldSimple[i].ax;
vertices[i].ay = oldSimple[i].ay;
- vertices[i].xx = 1;
- vertices[i].xy = 0;
- vertices[i].yx = 0;
- vertices[i].yy = 1;
- vertices[i].rotation = 0;
- vertices[i].rotationSpeed = 0;
- vertices[i].autoRotate = 0;
- vertices[i].animIdx = 0;
+ /*
vertices[i].frameDuration = oldSimple[i].lifeSpan;
vertices[i].frameCount = 1;
vertices[i].animT = oldSimple[i].t;
- vertices[i].color.r = 255;
- vertices[i].color.g = 255;
- vertices[i].color.b = 255;
- vertices[i].color.a = 255;
- }
- } else {
- for (int i=0; i<4; ++i) {
- vertices[i].x = 0;
- vertices[i].y = 0;
- vertices[i].t = -1;
- vertices[i].lifeSpan = 0;
- vertices[i].size = 0;
- vertices[i].endSize = 0;
- vertices[i].sx = 0;
- vertices[i].sy = 0;
- vertices[i].ax = 0;
- vertices[i].ay = 0;
- vertices[i].xx = 1;
- vertices[i].xy = 0;
- vertices[i].yx = 0;
- vertices[i].yy = 1;
- vertices[i].rotation = 0;
- vertices[i].rotationSpeed = 0;
- vertices[i].autoRotate = 0;
- vertices[i].animIdx = -1;
- vertices[i].frameDuration = 1;
- vertices[i].frameCount = 0;
- vertices[i].animT = -1;
- vertices[i].color.r = 0;//TODO:Some things never get used uninitialized. Consider dropping them here?
- vertices[i].color.g = 0;
- vertices[i].color.b = 0;
- vertices[i].color.a = 0;
+ */
}
}
- 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;
oldSimple += 4;
}
@@ -813,14 +789,95 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
return m_node;
}
+void QSGImageParticle::resize(int oldCount, int newCount)
+{
+ //If perf level changes at the same time as a resize, we reset instead of doing pending resize
+ if(!m_node)
+ return;
+ switch(perfLevel){
+ default:
+ case Sprites:
+ if(m_spriteEngine)
+ reset();//TODO: Handle sprite resizeing (have to shuffle the engine too...)
+ case Coloured:
+ case Deformable:
+ case Tabled:
+ if(!m_resizePending){
+ m_resizePendingUltra.resize(oldCount);
+ UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
+ for(int i=0; i<oldCount; i++)
+ m_resizePendingUltra[i] = &particles[i];
+ }
+ groupShuffle(m_resizePendingUltra, m_defaultUltra);
+ break;
+ case Simple:
+ if(!m_resizePending){
+ m_resizePendingSimple.resize(oldCount);
+ SimpleVertices *particles = (SimpleVertices *) m_node->geometry()->vertexData();
+ for(int i=0; i<oldCount; i++)
+ m_resizePendingSimple[i] = &particles[i];
+ }
+ groupShuffle(m_resizePendingSimple, m_defaultSimple);
+ break;
+ }
+ m_resizePending = true;
+}
+
+void QSGImageParticle::performPendingResize()
+{
+ m_resizePending = false;
+ if(!m_node)
+ return;
+ UltraVertices tmp1[m_count];//###More vast memcpys that will decrease performance
+ SimpleVertices tmp2[m_count];//###More vast memcpys that will decrease performance
+ switch(perfLevel){
+ case Sprites:
+ case Coloured:
+ case Deformable:
+ case Tabled:
+ Q_ASSERT(m_resizePendingUltra.size() == m_count);//XXX
+ for(int i=0; i<m_count; i++){
+ Q_ASSERT(m_resizePendingUltra[i]);
+ tmp1[i] = *m_resizePendingUltra[i];
+ }
+ m_node->setFlag(QSGNode::OwnsGeometry, false);
+ m_node->geometry()->allocate(m_count*4, m_count*6);
+ memcpy(m_node->geometry()->vertexData(), tmp1, sizeof(UltraVertices) * m_count);
+ m_node->setFlag(QSGNode::OwnsGeometry, true);
+ break;
+ case Simple:
+ Q_ASSERT(m_resizePendingSimple.size() == m_count);//XXX
+ for(int i=0; i<m_count; i++)
+ tmp2[i] = *m_resizePendingSimple[i];
+ m_node->setFlag(QSGNode::OwnsGeometry, false);
+ m_node->geometry()->allocate(m_count*4, m_count*6);
+ memcpy(m_node->geometry()->vertexData(), tmp2, sizeof(SimpleVertices) * m_count);
+ m_node->setFlag(QSGNode::OwnsGeometry, true);
+ break;
+ }
+ quint16 *indices = m_node->geometry()->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->setFlag(QSGNode::OwnsGeometry, true);
+}
+
QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
{
if(m_pleaseReset){
if(m_node){
if(perfLevel == 1){
qDebug() << "Beta";
- m_lastData = qMalloc(m_count*sizeof(SimpleVertices));//TODO: Account for count_changed possibility
- memcpy(m_lastData, m_node->geometry()->vertexData(), m_count * sizeof(SimpleVertices));//TODO: Multiple levels
+ 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;
@@ -832,6 +889,8 @@ QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
m_material = 0;
m_pleaseReset = false;
}
+ if(m_resizePending)
+ performPendingResize();
if(m_system && m_system->isRunning())
prepareNextFrame();
@@ -990,4 +1049,20 @@ void QSGImageParticle::load(QSGParticleData *d)
vertexCopy(*p->v4, d->pv);
}
+/*
+void QSGImageParticle::verticesUpgrade(void *prev, void *next)
+{
+ PerformanceLevel copyLevel = qMin(perfLevel, m_lastLevel);
+ switch(perfLevel){//Intentional fall-through
+ case Sprites:
+ if(copyLevel >= Sprites)
+ case Tabled:
+ case Deformable:
+ case Coloured:
+ }
+
+}
+*/
+
+
QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h
index c6ec4c2c6a..a644dd4216 100644
--- a/src/declarative/particles/qsgimageparticle_p.h
+++ b/src/declarative/particles/qsgimageparticle_p.h
@@ -167,7 +167,6 @@ public:
virtual void load(QSGParticleData*);
virtual void reload(QSGParticleData*);
- virtual void setCount(int c);
QDeclarativeListProperty<QSGSprite> sprites();
QSGSpriteEngine* spriteEngine() {return m_spriteEngine;}
@@ -298,12 +297,14 @@ protected:
void prepareNextFrame();
QSGGeometryNode* buildParticleNode();
QSGGeometryNode* buildSimpleParticleNode();
+ void resize(int oldCount, int newCount);
+ void performPendingResize();
private slots:
void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
private:
- //void vertexCopy(UltraVertex &b,const ParticleVertex& a);
+ //template <class T> void verticesUpgrade(IntermediateVertices* prev, T* next);//### Loses typessafety again...
IntermediateVertices* fetchIntermediateVertices(int pos);
bool m_do_reset;
@@ -345,6 +346,14 @@ private:
PerformanceLevel m_lastLevel;
void* m_lastData;
+ int m_lastCount;
+
+ //TODO: Some smart method that scales to multiple types better
+ bool m_resizePending;
+ QVector<UltraVertices*> m_resizePendingUltra;
+ QVector<SimpleVertices*> m_resizePendingSimple;
+ UltraVertices* m_defaultUltra;
+ SimpleVertices* m_defaultSimple;
};
QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgitemparticle.cpp b/src/declarative/particles/qsgitemparticle.cpp
index 42f0062148..1c6a8c4db5 100644
--- a/src/declarative/particles/qsgitemparticle.cpp
+++ b/src/declarative/particles/qsgitemparticle.cpp
@@ -87,6 +87,7 @@ void QSGItemParticle::give(QSGItem *item)
void QSGItemParticle::load(QSGParticleData* d)
{
+ Q_ASSERT(d);
int pos = particleTypeIndex(d);
m_data[pos] = d;
m_loadables << pos;
@@ -120,7 +121,7 @@ void QSGItemParticle::tick()
}else if(m_delegate){
m_items[pos] = qobject_cast<QSGItem*>(m_delegate->create(qmlContext(this)));
}
- if(m_items[pos]){
+ if(m_items[pos] && m_data[pos]){//###Data can be zero if creating an item leads to a reset - this screws things up.
m_items[pos]->setX(m_data[pos]->curX() - m_items[pos]->width()/2);//TODO: adjust for system?
m_items[pos]->setY(m_data[pos]->curY() - m_items[pos]->height()/2);
QSGItemParticleAttached* mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_items[pos]));
@@ -142,27 +143,22 @@ void QSGItemParticle::reload(QSGParticleData* d)
//No-op unless we start copying the data.
}
-void QSGItemParticle::setCount(int c)
-{
- QSGParticlePainter::setCount(c);//###Do we need our own?
- m_particleCount = c;
- reset();
-}
-
-int QSGItemParticle::count()
+void QSGItemParticle::resize(int oldCount, int newCount)
{
- return m_particleCount;
+ if(!m_system)
+ return;
+ groupShuffle(m_items, (QSGItem*)0);
+ groupShuffle(m_data, (QSGParticleData*)0);
}
void QSGItemParticle::reset()
{
QSGParticlePainter::reset();
//TODO: Cleanup items?
- m_items.resize(m_particleCount);
- m_data.resize(m_particleCount);
m_items.fill(0);
m_data.fill(0);
- //m_pendingItems.clear();//TODO: Should this be done? If so, Emit signal?
+ m_loadables.clear();
+ //deletables?
}
@@ -191,7 +187,7 @@ void QSGItemParticle::prepareNextFrame()
return;
//TODO: Size, better fade?
- for(int i=0; i<m_particleCount; i++){
+ for(int i=0; i<count(); i++){
QSGItem* item = m_items[i];
QSGParticleData* data = m_data[i];
if(!item || !data)
diff --git a/src/declarative/particles/qsgitemparticle_p.h b/src/declarative/particles/qsgitemparticle_p.h
index 3b7db519de..7cea63baa1 100644
--- a/src/declarative/particles/qsgitemparticle_p.h
+++ b/src/declarative/particles/qsgitemparticle_p.h
@@ -65,8 +65,6 @@ public:
virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
virtual void load(QSGParticleData*);
virtual void reload(QSGParticleData*);
- virtual void setCount(int c);
- virtual int count();
static QSGItemParticleAttached *qmlAttachedProperties(QObject *object);
QDeclarativeComponent* delegate() const
@@ -97,13 +95,13 @@ public slots:
protected:
virtual void reset();
+ virtual void resize(int oldCount, int newCount);
void prepareNextFrame();
private slots:
void tick();
private:
QList<QSGItem* > m_deletables;
QList< int > m_loadables;
- int m_particleCount;
bool m_fade;
QList<QSGItem*> m_pendingItems;
diff --git a/src/declarative/particles/qsgmodelparticle.cpp b/src/declarative/particles/qsgmodelparticle.cpp
index f87c0d31b9..aba68c6ce4 100644
--- a/src/declarative/particles/qsgmodelparticle.cpp
+++ b/src/declarative/particles/qsgmodelparticle.cpp
@@ -201,25 +201,17 @@ void QSGModelParticle::reload(QSGParticleData* d)
//No-op unless we start copying the data.
}
-void QSGModelParticle::setCount(int c)
+void QSGModelParticle::resize(int oldCount, int newCount)
{
- QSGParticlePainter::setCount(c);//###Do we need our own?
- m_particleCount = c;
- reset();
-}
-
-int QSGModelParticle::count()
-{
- return m_particleCount;
+ groupShuffle(m_items, (QSGItem *) 0);
+ groupShuffle(m_data, (QSGParticleData*) 0);
+ groupShuffle(m_idx, -1);
}
void QSGModelParticle::reset()
{
QSGParticlePainter::reset();
//TODO: Cleanup items?
- m_items.resize(m_particleCount);
- m_data.resize(m_particleCount);
- m_idx.resize(m_particleCount);
m_items.fill(0);
m_data.fill(0);
m_idx.fill(-1);
@@ -253,7 +245,7 @@ void QSGModelParticle::prepareNextFrame()
return;
//TODO: Size, better fade?
- for(int i=0; i<m_particleCount; i++){
+ for(int i=0; i<count(); i++){
QSGItem* item = m_items[i];
QSGParticleData* data = m_data[i];
if(!item || !data)
diff --git a/src/declarative/particles/qsgmodelparticle_p.h b/src/declarative/particles/qsgmodelparticle_p.h
index 04533a73ce..cef2008b1a 100644
--- a/src/declarative/particles/qsgmodelparticle_p.h
+++ b/src/declarative/particles/qsgmodelparticle_p.h
@@ -76,8 +76,6 @@ public:
virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
virtual void load(QSGParticleData*);
virtual void reload(QSGParticleData*);
- virtual void setCount(int c);
- virtual int count();
static QSGModelParticleAttached *qmlAttachedProperties(QObject *object);
signals:
@@ -93,6 +91,7 @@ public slots:
void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();}
protected:
virtual void reset();
+ virtual void resize(int oldCount, int newCount);
void prepareNextFrame();
private slots:
void updateCount();
@@ -104,7 +103,6 @@ private:
QVariant m_dataSource;
QList<QSGItem*> m_deletables;
QList< int > m_requests;
- int m_particleCount;
bool m_fade;
QList<QSGItem*> m_pendingItems;
diff --git a/src/declarative/particles/qsgparticleaffector.cpp b/src/declarative/particles/qsgparticleaffector.cpp
index 96c5cfb25b..5b0936cc40 100644
--- a/src/declarative/particles/qsgparticleaffector.cpp
+++ b/src/declarative/particles/qsgparticleaffector.cpp
@@ -79,22 +79,22 @@ void QSGParticleAffector::affectSystem(qreal dt)
m_groups << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned?
m_updateIntSet = false;
}
- //foreach(ParticleData* d, m_system->m_data){
- for(int i=0; i<m_system->m_particle_count; i++){
- QSGParticleData* d = m_system->m_data[i];
- if(!d || (m_onceOff && m_onceOffed.contains(d->systemIndex)))
- continue;
- if(m_groups.isEmpty() || m_groups.contains(d->group)){
- //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
- || 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 << d->systemIndex;
- if(m_signal)
- emit affected(curPos.x(), curPos.y());
+ foreach(QSGParticleGroupData* gd, m_system->m_groupData){
+ foreach(QSGParticleData* d, gd->data){
+ if(!d || (m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index))))
+ continue;
+ if(m_groups.isEmpty() || m_groups.contains(d->group)){
+ //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
+ || 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());
+ }
}
}
}
@@ -108,10 +108,10 @@ bool QSGParticleAffector::affectParticle(QSGParticleData *d, qreal dt)
return false;
}
-void QSGParticleAffector::reset(int idx)
+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(idx);
+ m_onceOffed.remove(qMakePair(pd->group, pd->index));
}
void QSGParticleAffector::updateOffsets()
diff --git a/src/declarative/particles/qsgparticleaffector_p.h b/src/declarative/particles/qsgparticleaffector_p.h
index b299158069..7418760941 100644
--- a/src/declarative/particles/qsgparticleaffector_p.h
+++ b/src/declarative/particles/qsgparticleaffector_p.h
@@ -65,7 +65,7 @@ class QSGParticleAffector : public QSGItem
public:
explicit QSGParticleAffector(QSGItem *parent = 0);
virtual void affectSystem(qreal dt);
- virtual void reset(int systemIdx);//As some store their own data per idx?
+ virtual void reset(QSGParticleData*);//As some store their own data per particle?
QSGParticleSystem* system() const
{
return m_system;
@@ -108,7 +108,7 @@ signals:
void shapeChanged(QSGParticleExtruder* arg);
- void affected(qreal x, qreal y);//###Idx too?
+ void affected(qreal x, qreal y);
void signalChanged(bool arg);
public slots:
@@ -174,7 +174,7 @@ protected:
QPointF m_offset;
private:
QSet<int> m_groups;
- QSet<int> m_onceOffed;
+ QSet<QPair<int, int> > m_onceOffed;
bool m_updateIntSet;
bool m_onceOff;
diff --git a/src/declarative/particles/qsgparticlepainter.cpp b/src/declarative/particles/qsgparticlepainter.cpp
index a5e8c0a3e6..0d369fd4d8 100644
--- a/src/declarative/particles/qsgparticlepainter.cpp
+++ b/src/declarative/particles/qsgparticlepainter.cpp
@@ -44,7 +44,7 @@
QT_BEGIN_NAMESPACE
QSGParticlePainter::QSGParticlePainter(QSGItem *parent) :
QSGItem(parent),
- m_system(0)
+ m_system(0), m_count(0), m_lastStart(0)
{
connect(this, SIGNAL(xChanged()),
this, SLOT(calcSystemOffset()));
@@ -67,7 +67,7 @@ void QSGParticlePainter::setSystem(QSGParticleSystem *arg)
if (m_system != arg) {
m_system = arg;
if(m_system){
- m_system->registerParticleType(this);
+ m_system->registerParticlePainter(this);
connect(m_system, SIGNAL(xChanged()),
this, SLOT(calcSystemOffset()));
connect(m_system, SIGNAL(yChanged()),
@@ -89,15 +89,24 @@ void QSGParticlePainter::reload(QSGParticleData*)
void QSGParticlePainter::reset()
{
//Have to every time because what it's emitting may have changed and that affects particleTypeIndex
- m_particleStarts.clear();
- m_lastStart = 0;
+ if(m_system)
+ updateParticleStarts();
}
+void QSGParticlePainter::resize(int, int)
+{
+}
+
+
void QSGParticlePainter::setCount(int c)
{
+ Q_ASSERT(c >= 0); //XXX
if(c == m_count)
return;
+ int lastCount = m_count;
m_count = c;
+ resize(lastCount, m_count);//### is virtual needed? Or should I just use the signal?
+ updateParticleStarts();
emit countChanged();
}
@@ -107,14 +116,27 @@ int QSGParticlePainter::count()
}
-int QSGParticlePainter::particleTypeIndex(QSGParticleData* d)
+void QSGParticlePainter::updateParticleStarts()
{
- if(!m_particleStarts.contains(d->group)){
- m_particleStarts.insert(d->group, m_lastStart);
- m_lastStart += m_system->m_groupData[d->group]->size;
+ 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;
}
- int ret = m_particleStarts[d->group] + d->particleIndex;
- Q_ASSERT(ret >=0 && ret < m_count);//XXX: Possibly shouldn't assert, but bugs here were hard to find in the past
+}
+
+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;
}
@@ -129,8 +151,8 @@ void QSGParticlePainter::calcSystemOffset()
//Reload all particles//TODO: Necessary?
foreach(const QString &g, m_particles){
int gId = m_system->m_groupIds[g];
- for(int i=0; i<m_system->m_groupData[gId]->size; i++)
- reload(m_system->m_data[m_system->m_groupData[gId]->start + i]);
+ 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 8f1e13b70f..6657d57501 100644
--- a/src/declarative/particles/qsgparticlepainter_p.h
+++ b/src/declarative/particles/qsgparticlepainter_p.h
@@ -63,8 +63,8 @@ public:
explicit QSGParticlePainter(QSGItem *parent = 0);
virtual void load(QSGParticleData*);
virtual void reload(QSGParticleData*);
- virtual void setCount(int c);
- virtual int count();
+ void setCount(int c);
+ int count();
QSGParticleSystem* system() const
{
return m_system;
@@ -76,7 +76,6 @@ public:
return m_particles;
}
- int particleTypeIndex(QSGParticleData*);
signals:
void countChanged();
void systemChanged(QSGParticleSystem* arg);
@@ -95,25 +94,23 @@ void setParticles(QStringList arg)
}
private slots:
void calcSystemOffset();
+ void updateParticleStarts();
+
protected:
virtual void reset();
virtual void componentComplete();
-// virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *){
-// qDebug() << "Shouldn't be here..." << this;
-// return 0;
-// }
QSGParticleSystem* m_system;
friend class QSGParticleSystem;
int m_count;
bool m_pleaseReset;
QStringList m_particles;
- QHash<int,int> m_particleStarts;
+ QHash<int,QPair<int, int> > m_particleStarts; //Group, size, idx
int m_lastStart;
QPointF m_systemOffset;
- template <typename VertexStruct>
+ template <typename VertexStruct>//just convenience
void vertexCopy(VertexStruct &b, const ParticleVertex& a)
{
b.x = a.x - m_systemOffset.x();
@@ -128,6 +125,36 @@ protected:
b.ay = a.ay;
}
+ //###Abstracted primarily for code reuse. Demote to subclasses?
+ int particleTypeIndex(QSGParticleData*);
+ virtual void resize(int oldCount, int newCount);
+ template <typename T>
+ void groupShuffle(QVector<T> &v, const T& zero)//Must be called inside resize
+ {
+ //TODO: In place shuffling because it's faster
+ QVector<T> v0(v);
+ v.clear();
+ v.resize(m_count);
+ int 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];
+ for(int i=0; i<gd->data.size(); i++){//TODO: When group didn't exist before
+ int newIdx = lastStart + i;//Have to make the same way as in updateParticleStarts
+ if(i >= m_particleStarts[gIdx].first || v0.size() <= m_particleStarts[gIdx].second + i)
+ v[newIdx] = zero;
+ else
+ v[newIdx] = v0[m_particleStarts[gIdx].second + i];
+ }
+ lastStart += gd->size;
+ }
+ }
+
private:
};
diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp
index 4a8f75bb04..a93f2a5eba 100644
--- a/src/declarative/particles/qsgparticlesystem.cpp
+++ b/src/declarative/particles/qsgparticlesystem.cpp
@@ -52,8 +52,7 @@ QT_BEGIN_NAMESPACE
QSGParticleData::QSGParticleData()
: group(0)
, e(0)
- , particleIndex(0)
- , systemIndex(0)
+ , index(0)
{
pv.x = 0;
pv.y = 0;
@@ -71,174 +70,202 @@ QSGParticleSystem::QSGParticleSystem(QSGItem *parent) :
, m_startTime(0), m_overwrite(false)
, m_componentComplete(false)
{
- m_groupIds = QHash<QString, int>();
+ QSGParticleGroupData* gd = new QSGParticleGroupData;//Default group
+ m_groupData.insert(0,gd);
+ m_groupIds.insert("",0);
+ m_nextGroupId = 1;
+
+ connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
+ this, SLOT(loadPainter(QObject*)));
}
-void QSGParticleSystem::registerParticleType(QSGParticlePainter* p)
+void QSGParticleSystem::registerParticlePainter(QSGParticlePainter* p)
{
- m_particles << QPointer<QSGParticlePainter>(p);//###Set or uniqueness checking?
- reset();
+ //TODO: a way to Unregister emitters, painters and affectors
+ m_particlePainters << QPointer<QSGParticlePainter>(p);//###Set or uniqueness checking?
+ connect(p, SIGNAL(particlesChanged(QStringList)),
+ &m_painterMapper, SLOT(map()));
+ loadPainter(p);
+ p->update();//###Initial update here?
}
void QSGParticleSystem::registerParticleEmitter(QSGParticleEmitter* e)
{
m_emitters << QPointer<QSGParticleEmitter>(e);//###How to get them out?
connect(e, SIGNAL(particleCountChanged()),
- this, SLOT(countChanged()));
+ this, SLOT(emittersChanged()));
connect(e, SIGNAL(particleChanged(QString)),
- this, SLOT(countChanged()));
- reset();
+ this, SLOT(emittersChanged()));
+ emittersChanged();
+ e->reset();//Start, so that starttime factors appropriately
}
void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a)
{
m_affectors << QPointer<QSGParticleAffector>(a);
- //reset();//TODO: Slim down the huge batch of resets at the start
}
-void QSGParticleSystem::countChanged()
+void QSGParticleSystem::loadPainter(QObject *p)
{
- reset();//Need to give Particles new Count
-}
+ if(!m_componentComplete)
+ return;
-void QSGParticleSystem::setRunning(bool arg)
-{
- if (m_running != arg) {
- m_running = arg;
- emit runningChanged(arg);
- reset();
+ QSGParticlePainter* painter = qobject_cast<QSGParticlePainter*>(p);
+ Q_ASSERT(painter);//XXX
+ foreach(QSGParticleGroupData* sg, m_groupData)
+ sg->painters.remove(painter);
+ int particleCount = 0;
+ if(painter->particles().isEmpty()){//Uses default particle
+ particleCount += m_groupData[0]->size;
+ m_groupData[0]->painters << painter;
+ }else{
+ foreach(const QString &group, painter->particles()){
+ particleCount += m_groupData[m_groupIds[group]]->size;
+ m_groupData[m_groupIds[group]]->painters << painter;
+ }
}
+ painter->setCount(particleCount);
+ painter->update();//###Initial update here?
+ return;
}
-void QSGParticleSystem::componentComplete()
-{
- QSGItem::componentComplete();
- m_componentComplete = true;
- if(!m_emitters.isEmpty() && !m_particles.isEmpty())
- reset();
-}
-
-void QSGParticleSystem::initializeSystem()
+void QSGParticleSystem::emittersChanged()
{
- int oldCount = m_particle_count;
- m_particle_count = 0;//TODO: Only when changed?
-
- //### Reset the data too?
- for(int i=0; i<oldCount; i++){
- if(m_data[i]){
- delete m_data[i];
- m_data[i] = 0;
- }
- }
+ if(!m_componentComplete)
+ return;
- for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++)
- delete (*iter);
- m_groupData.clear();
- m_groupIds.clear();
+ m_emitters.removeAll(0);
- GroupData* gd = new GroupData;//Default group
- gd->size = 0;
- gd->start = -1;
- gd->nextIdx = 0;
- m_groupData.insert(0,gd);
- m_groupIds.insert("",0);
- m_nextGroupId = 1;
+ //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;
- if(!m_emitters.count() || !m_particles.count())
- return;
+ 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;
- foreach(QSGParticleEmitter* e, m_emitters){
+ 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
- GroupData* gd = new GroupData;
- gd->size = 0;
- gd->start = -1;
- gd->nextIdx = 0;
+ QSGParticleGroupData* gd = new QSGParticleGroupData;
int id = m_nextGroupId++;
m_groupIds.insert(e->particle(), id);
m_groupData.insert(id, gd);
}
m_groupData[m_groupIds[e->particle()]]->size += e->particleCount();
+ m_particle_count += e->particleCount();
+ //###: Cull emptied groups?
}
- for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++){
- (*iter)->start = m_particle_count;
- m_particle_count += (*iter)->size;
- }
- m_data.resize(m_particle_count);
- for(int i=oldCount; i<m_particle_count; i++)
- m_data[i] = 0;//setup new ones
-
- if(m_particle_count > 16000)
- qWarning() << "Particle system contains a vast number of particles (>16000). Expect poor performance";
-
- foreach(QSGParticlePainter* particle, m_particles){
- int particleCount = 0;
- if(particle->particles().isEmpty()){//Uses default particle
- particleCount += m_groupData[0]->size;
- m_groupData[0]->types << particle;
- }else{
- foreach(const QString &group, particle->particles()){
- particleCount += m_groupData[m_groupIds[group]]->size;
- m_groupData[m_groupIds[group]]->types << particle;
+ 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);
}
+ */
}
- particle->setCount(particleCount);
- particle->m_pleaseReset = true;
}
+ foreach(QSGParticlePainter *p, m_particlePainters)
+ loadPainter(p);
- m_timestamp.start();
- m_initialized = true;
- emit systemInitialized();
- qDebug() << "System Initialized. Size:" << m_particle_count;
+ 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";
+}
+
+void QSGParticleSystem::setRunning(bool arg)
+{
+ if (m_running != arg) {
+ m_running = arg;
+ emit runningChanged(arg);
+ reset();
+ }
}
-void QSGParticleSystem::reset()
+void QSGParticleSystem::componentComplete()
+{
+ QSGItem::componentComplete();
+ m_componentComplete = true;
+ //if(!m_emitters.isEmpty() && !m_particlePainters.isEmpty())
+ reset();
+}
+
+void QSGParticleSystem::reset()//TODO: Needed?
{
if(!m_componentComplete)
- return;//Batch starting reset()s a little
+ return;
+
//Clear guarded pointers which have been deleted
int cleared = 0;
cleared += m_emitters.removeAll(0);
- cleared += m_particles.removeAll(0);
+ cleared += m_particlePainters.removeAll(0);
cleared += m_affectors.removeAll(0);
//qDebug() << "Reset" << m_emitters.count() << m_particles.count() << "Cleared" << cleared;
- foreach(QSGParticlePainter* p, m_particles)
- p->reset();
- foreach(QSGParticleEmitter* e, m_emitters)
- e->reset();
+
+ emittersChanged();
+
+ //TODO: Reset data
+// foreach(QSGParticlePainter* p, m_particlePainters)
+// p->reset();
+// foreach(QSGParticleEmitter* e, m_emitters)
+// e->reset();
+ //### Do affectors need reset too?
+
if(!m_running)
return;
- initializeSystem();
- foreach(QSGParticlePainter* p, m_particles)
- p->update();
- foreach(QSGParticleEmitter* e, m_emitters)
- e->emitWindow(0);//Start, so that starttime factors appropriately
+
+ foreach(QSGParticlePainter *p, m_particlePainters){
+ loadPainter(p);
+ p->reset();
+ }
+
+ m_timestamp.start();//TODO: Better placement
+ m_initialized = true;
}
QSGParticleData* QSGParticleSystem::newDatum(int groupId)
{
+
Q_ASSERT(groupId < m_groupData.count());//XXX shouldn't really be an assert
Q_ASSERT(m_groupData[groupId]->size);
- int nextIdx = m_groupData[groupId]->start + m_groupData[groupId]->nextIdx++;
+
if( m_groupData[groupId]->nextIdx >= m_groupData[groupId]->size)
m_groupData[groupId]->nextIdx = 0;
+ int nextIdx = m_groupData[groupId]->nextIdx++;
- Q_ASSERT(nextIdx < m_data.size());
+ Q_ASSERT(nextIdx < m_groupData[groupId]->size);
QSGParticleData* ret;
- if(m_data[nextIdx]){//Recycle, it's faster.
- ret = m_data[nextIdx];
+ 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?
}else{
ret = new QSGParticleData;
- m_data[nextIdx] = ret;
+ m_groupData[groupId]->data[nextIdx] = ret;
}
ret->system = this;
- ret->systemIndex = nextIdx;
- ret->particleIndex = nextIdx - m_groupData[groupId]->start;
+ ret->index = nextIdx;
ret->group = groupId;
return ret;
}
@@ -254,8 +281,8 @@ void QSGParticleSystem::emitParticle(QSGParticleData* pd)
foreach(QSGParticleAffector *a, m_affectors)
if(a && a->m_needsReset)
- a->reset(pd->systemIndex);
- foreach(QSGParticlePainter* p, m_groupData[pd->group]->types)
+ a->reset(pd);
+ foreach(QSGParticlePainter* p, m_groupData[pd->group]->painters)
if(p)
p->load(pd);
}
@@ -285,7 +312,7 @@ qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p)
if(a)
a->affectSystem(dt);
foreach(QSGParticleData* d, m_needsReset)
- foreach(QSGParticlePainter* p, m_groupData[d->group]->types)
+ foreach(QSGParticlePainter* p, m_groupData[d->group]->painters)
if(p && d)
p->reload(d);
}
diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h
index 9730ff35e8..4c690ff7a8 100644
--- a/src/declarative/particles/qsgparticlesystem_p.h
+++ b/src/declarative/particles/qsgparticlesystem_p.h
@@ -47,6 +47,7 @@
#include <QVector>
#include <QHash>
#include <QPointer>
+#include <QSignalMapper>
QT_BEGIN_HEADER
@@ -61,11 +62,14 @@ class QSGParticlePainter;
class QSGParticleData;
-struct GroupData{
+class QSGParticleGroupData{
+public:
+ QSGParticleGroupData():size(0),nextIdx(0)
+ {}
int size;
- int start;
int nextIdx;
- QList<QSGParticlePainter*> types;
+ QSet<QSGParticlePainter*> painters;
+ QVector<QSGParticleData*> data;
};
class QSGParticleSystem : public QSGItem
@@ -131,20 +135,20 @@ protected:
void componentComplete();
private slots:
- void countChanged();
+ 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;
- QVector<QSGParticleData*> m_data;
QSet<QSGParticleData*> m_needsReset;
QHash<QString, int> m_groupIds;
- QHash<int, GroupData*> m_groupData;//id, size, start
+ QHash<int, QSGParticleGroupData*> m_groupData;
qint64 m_timeInt;
bool m_initialized;
- void registerParticleType(QSGParticlePainter* p);
+ void registerParticlePainter(QSGParticlePainter* p);
void registerParticleEmitter(QSGParticleEmitter* e);
void registerParticleAffector(QSGParticleAffector* a);
bool overwrite() const
@@ -158,12 +162,15 @@ private:
bool m_running;
QList<QPointer<QSGParticleEmitter> > m_emitters;
QList<QPointer<QSGParticleAffector> > m_affectors;
- QList<QPointer<QSGParticlePainter> > m_particles;
+ 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;
};
//TODO: Clean up all this into ParticleData
@@ -211,10 +218,9 @@ public:
qreal curSY() const;
int group;
- QSGParticleEmitter* e;
+ QSGParticleEmitter* e;//### Needed?
QSGParticleSystem* system;
- int particleIndex;
- int systemIndex;
+ int index;
void debugDump();
bool stillAlive();
diff --git a/src/declarative/particles/qsgspritegoal.cpp b/src/declarative/particles/qsgspritegoal.cpp
index 8dc98ae314..c97bfd1f26 100644
--- a/src/declarative/particles/qsgspritegoal.cpp
+++ b/src/declarative/particles/qsgspritegoal.cpp
@@ -81,7 +81,7 @@ 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]->types)
+ foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->painters)
if(qobject_cast<QSGImageParticle*>(p))
engine = qobject_cast<QSGImageParticle*>(p)->spriteEngine();
if(!engine)
@@ -89,8 +89,8 @@ bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt)
if(m_goalIdx == -2 || engine != m_lastEngine)
updateStateIndex(engine);
- if(engine->spriteState(d->particleIndex) != m_goalIdx){
- engine->setGoal(m_goalIdx, d->particleIndex, m_jump);
+ if(engine->spriteState(d->index) != m_goalIdx){
+ engine->setGoal(m_goalIdx, d->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/qsgturbulence.cpp b/src/declarative/particles/qsgturbulence.cpp
index 476db9c4b0..bb46b99f0d 100644
--- a/src/declarative/particles/qsgturbulence.cpp
+++ b/src/declarative/particles/qsgturbulence.cpp
@@ -128,30 +128,34 @@ void QSGTurbulenceAffector::affectSystem(qreal dt)
m_lastT += period;
}
- foreach(QSGParticleData *d, m_system->m_data){
- if(!d || !activeGroup(d->group))
+ foreach(QSGParticleGroupData *gd, m_system->m_groupData){
+ if(!activeGroup(m_system->m_groupData.key(gd)))//TODO: Surely this can be done better
return;
- qreal fx = 0.0;
- qreal fy = 0.0;
- QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset
- QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y());
- QSet<QPair<int, int> > nodes;
- nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y()));
- nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y()));
- 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)))
- 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){
- d->setInstantaneousSX(d->curSX()+ fx * dt);
- d->setInstantaneousSY(d->curSY()+ fy * dt);
- m_system->m_needsReset << d;
+ foreach(QSGParticleData *d, gd->data){
+ if(!d || !activeGroup(d->group))
+ return;
+ qreal fx = 0.0;
+ qreal fy = 0.0;
+ QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset
+ QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y());
+ QSet<QPair<int, int> > nodes;
+ nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y()));
+ nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y()));
+ 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)))
+ 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){
+ d->setInstantaneousSX(d->curSX()+ fx * dt);
+ d->setInstantaneousSY(d->curSY()+ fy * dt);
+ m_system->m_needsReset << d;
+ }
}
}
}