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
This commit is contained in:
parent
7b22f02961
commit
fab0da3853
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}"
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
void QSGItemParticle::resize(int oldCount, int newCount)
|
||||
{
|
||||
QSGParticlePainter::setCount(c);//###Do we need our own?
|
||||
m_particleCount = c;
|
||||
reset();
|
||||
}
|
||||
|
||||
int QSGItemParticle::count()
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
|||
}
|
||||
|
||||
|
||||
void QSGParticlePainter::updateParticleStarts()
|
||||
{
|
||||
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 QSGParticlePainter::particleTypeIndex(QSGParticleData* d)
|
||||
{
|
||||
if(!m_particleStarts.contains(d->group)){
|
||||
m_particleStarts.insert(d->group, m_lastStart);
|
||||
m_lastStart += m_system->m_groupData[d->group]->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
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
};
|
||||
|
||||
|
|
|
@ -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,34 +70,126 @@ 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;
|
||||
|
||||
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::emittersChanged()
|
||||
{
|
||||
if(!m_componentComplete)
|
||||
return;
|
||||
|
||||
m_emitters.removeAll(0);
|
||||
|
||||
//Recalculate all counts, as emitter 'particle' may have changed as well
|
||||
//### Worth tracking previous 'particle' per emitter to do partial recalculations?
|
||||
m_particle_count = 0;
|
||||
|
||||
int previousGroups = m_nextGroupId;
|
||||
QVector<int> previousSizes;
|
||||
previousSizes.resize(previousGroups);
|
||||
for(int i=0; i<previousGroups; i++)
|
||||
previousSizes[i] = m_groupData[i]->size;
|
||||
for(int i=0; i<previousGroups; i++)
|
||||
m_groupData[i]->size = 0;
|
||||
|
||||
foreach(QSGParticleEmitter* e, m_emitters){//Populate groups and set sizes.
|
||||
if(!m_groupIds.contains(e->particle())
|
||||
|| (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier
|
||||
QSGParticleGroupData* gd = new QSGParticleGroupData;
|
||||
int id = m_nextGroupId++;
|
||||
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?
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
foreach(QSGParticlePainter *p, m_particlePainters)
|
||||
loadPainter(p);
|
||||
|
||||
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)
|
||||
|
@ -114,131 +205,67 @@ void QSGParticleSystem::componentComplete()
|
|||
{
|
||||
QSGItem::componentComplete();
|
||||
m_componentComplete = true;
|
||||
if(!m_emitters.isEmpty() && !m_particles.isEmpty())
|
||||
reset();
|
||||
//if(!m_emitters.isEmpty() && !m_particlePainters.isEmpty())
|
||||
reset();
|
||||
}
|
||||
|
||||
void QSGParticleSystem::initializeSystem()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++)
|
||||
delete (*iter);
|
||||
m_groupData.clear();
|
||||
m_groupIds.clear();
|
||||
|
||||
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;
|
||||
|
||||
if(!m_emitters.count() || !m_particles.count())
|
||||
return;
|
||||
|
||||
foreach(QSGParticleEmitter* e, m_emitters){
|
||||
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;
|
||||
int id = m_nextGroupId++;
|
||||
m_groupIds.insert(e->particle(), id);
|
||||
m_groupData.insert(id, gd);
|
||||
}
|
||||
m_groupData[m_groupIds[e->particle()]]->size += e->particleCount();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
particle->setCount(particleCount);
|
||||
particle->m_pleaseReset = true;
|
||||
}
|
||||
|
||||
m_timestamp.start();
|
||||
m_initialized = true;
|
||||
emit systemInitialized();
|
||||
qDebug() << "System Initialized. Size:" << m_particle_count;
|
||||
}
|
||||
|
||||
void QSGParticleSystem::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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue