From fab0da3853efc5ef2f1a507f7d054b8a0dd3b9ac Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 15 Jun 2011 18:52:26 +1000 Subject: 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 --- .../declarative/particles/custom/fireworks.qml | 37 +-- examples/declarative/particles/custom/shader.qml | 84 +++++++ .../particles/trails/dynamicemitters.qml | 8 +- src/declarative/particles/qsgcustomparticle.cpp | 143 ++++++------ src/declarative/particles/qsgcustomparticle_p.h | 10 +- src/declarative/particles/qsgfollowemitter.cpp | 12 +- src/declarative/particles/qsgimageparticle.cpp | 255 +++++++++++++-------- src/declarative/particles/qsgimageparticle_p.h | 13 +- src/declarative/particles/qsgitemparticle.cpp | 24 +- src/declarative/particles/qsgitemparticle_p.h | 4 +- src/declarative/particles/qsgmodelparticle.cpp | 18 +- src/declarative/particles/qsgmodelparticle_p.h | 4 +- src/declarative/particles/qsgparticleaffector.cpp | 36 +-- src/declarative/particles/qsgparticleaffector_p.h | 6 +- src/declarative/particles/qsgparticlepainter.cpp | 46 +++- src/declarative/particles/qsgparticlepainter_p.h | 45 +++- src/declarative/particles/qsgparticlesystem.cpp | 235 ++++++++++--------- src/declarative/particles/qsgparticlesystem_p.h | 28 ++- src/declarative/particles/qsgspritegoal.cpp | 6 +- src/declarative/particles/qsgturbulence.cpp | 50 ++-- 20 files changed, 655 insertions(+), 409 deletions(-) create mode 100644 examples/declarative/particles/custom/shader.qml 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 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; iisRunning()) 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; isetFlag(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; isetFlag(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; pindexDataAsUShort(); @@ -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 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; im_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 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; pvertexData(); + for (int p=0; pindexDataAsUShort(); for (int i=0; i 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; igeometry()->vertexData(); + for(int i=0; isetFlag(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; isetFlag(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; isetFlag(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 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 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 m_resizePendingUltra; + QVector 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(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(qmlAttachedPropertiesObject(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_deletables; QList< int > m_loadables; - int m_particleCount; bool m_fade; QList 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_deletables; QList< int > m_requests; - int m_particleCount; bool m_fade; QList 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; im_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 m_groups; - QSet m_onceOffed; + QSet > 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 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(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; im_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 m_particleStarts; + QHash > m_particleStarts; //Group, size, idx int m_lastStart; QPointF m_systemOffset; - template + template //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 + void groupShuffle(QVector &v, const T& zero)//Must be called inside resize + { + //TODO: In place shuffling because it's faster + QVector v0(v); + v.clear(); + v.resize(m_count); + int lastStart = 0; + QList 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; idata.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(); + 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(p);//###Set or uniqueness checking? - reset(); + //TODO: a way to Unregister emitters, painters and affectors + m_particlePainters << QPointer(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(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(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(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::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 previousSizes; + previousSizes.resize(previousGroups); + for(int i=0; isize; + for(int i=0; isize = 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::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 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]; isize; 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 #include #include +#include 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 types; + QSet painters; + QVector 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 m_data; QSet m_needsReset; QHash m_groupIds; - QHash m_groupData;//id, size, start + QHash 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 > m_emitters; QList > m_affectors; - QList > m_particles; + QList > m_particlePainters; QList > 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(p)) engine = qobject_cast(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 > 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 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 > 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 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; + } } } } -- cgit v1.2.3