From ca6d931bb7714de7293dab5d14a19f74dc98d103 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 1 Sep 2011 15:27:05 +1000 Subject: Animation-like API for ParticleSystem Includes skipping rendering when paused. Change-Id: I353ac415fb877917d46ba1832ad9cb5a84640b57 Reviewed-on: http://codereview.qt.nokia.com/4041 Reviewed-by: Martin Jones --- src/declarative/particles/qsgcustomparticle.cpp | 13 +- src/declarative/particles/qsgimageparticle.cpp | 14 +- src/declarative/particles/qsgparticleemitter.cpp | 6 +- src/declarative/particles/qsgparticleemitter_p.h | 14 +- src/declarative/particles/qsgparticlesystem.cpp | 255 ++++++++++++++--------- src/declarative/particles/qsgparticlesystem_p.h | 22 +- 6 files changed, 201 insertions(+), 123 deletions(-) (limited to 'src/declarative') diff --git a/src/declarative/particles/qsgcustomparticle.cpp b/src/declarative/particles/qsgcustomparticle.cpp index 11bc7e35fb..00e3ec185b 100644 --- a/src/declarative/particles/qsgcustomparticle.cpp +++ b/src/declarative/particles/qsgcustomparticle.cpp @@ -413,13 +413,14 @@ QSGNode *QSGCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat m_dirtyData = false; } - if (m_system && m_system->isRunning()) + if (m_system && m_system->isRunning() && !m_system->isPaused()){ prepareNextFrame(); - if (m_rootNode){ - update(); - //### Should I be using dirty geometry too/instead? - foreach (QSGShaderEffectNode* node, m_nodes) - node->markDirty(QSGNode::DirtyMaterial); //done in buildData? + if (m_rootNode) { + update(); + //### Should I be using dirty geometry too/instead? + foreach (QSGGeometryNode* node, m_nodes) + node->markDirty(QSGNode::DirtyMaterial);//done in buildData? + } } return m_rootNode; diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp index ec6c7375dc..5e0e97797f 100644 --- a/src/declarative/particles/qsgimageparticle.cpp +++ b/src/declarative/particles/qsgimageparticle.cpp @@ -53,7 +53,6 @@ #include QT_BEGIN_NAMESPACE - //###Switch to define later, for now user-friendly (no compilation) debugging is worth it DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG) @@ -1024,13 +1023,14 @@ QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) m_pleaseReset = false; } - if (m_system && m_system->isRunning()) + if (m_system && m_system->isRunning() && !m_system->isPaused()){ prepareNextFrame(); - if (m_rootNode){ - update(); - //### Should I be using dirty geometry too/instead? - foreach (QSGGeometryNode* node, m_nodes) - node->markDirty(QSGNode::DirtyMaterial); + if (m_rootNode) { + update(); + //### Should I be using dirty geometry too/instead? + foreach (QSGGeometryNode* node, m_nodes) + node->markDirty(QSGNode::DirtyMaterial); + } } return m_rootNode; diff --git a/src/declarative/particles/qsgparticleemitter.cpp b/src/declarative/particles/qsgparticleemitter.cpp index bf4f5701e5..2a76f77cf1 100644 --- a/src/declarative/particles/qsgparticleemitter.cpp +++ b/src/declarative/particles/qsgparticleemitter.cpp @@ -317,6 +317,11 @@ void QSGParticleEmitter::setSpeedFromMovement(qreal t) emit speedFromMovementChanged(); } +void QSGParticleEmitter::reset() +{ + m_reset_last = true; +} + void QSGParticleEmitter::emitWindow(int timeStamp) { if (m_system == 0) @@ -346,7 +351,6 @@ void QSGParticleEmitter::emitWindow(int timeStamp) } qreal time = timeStamp / 1000.; - qreal particleRatio = 1. / m_particlesPerSecond; qreal pt = m_last_emission; diff --git a/src/declarative/particles/qsgparticleemitter_p.h b/src/declarative/particles/qsgparticleemitter_p.h index 2c75fbfe22..cd14fa55e9 100644 --- a/src/declarative/particles/qsgparticleemitter_p.h +++ b/src/declarative/particles/qsgparticleemitter_p.h @@ -257,17 +257,17 @@ public slots: } void setOverWrite(bool arg) -{ - if (m_overwrite != arg) { - m_overwrite = arg; -emit overwriteChanged(arg); -} -} + { + if (m_overwrite != arg) { + m_overwrite = arg; + emit overwriteChanged(arg); + } + } public: int particleCount() const; - virtual void reset(){;} + virtual void reset(); QSGParticleExtruder* extruder() const { return m_extruder; diff --git a/src/declarative/particles/qsgparticlesystem.cpp b/src/declarative/particles/qsgparticlesystem.cpp index efd6c22bd9..8bd39984ee 100644 --- a/src/declarative/particles/qsgparticlesystem.cpp +++ b/src/declarative/particles/qsgparticlesystem.cpp @@ -54,6 +54,8 @@ #include QT_BEGIN_NAMESPACE +//###Switch to define later, for now user-friendly (no compilation) debugging is worth it +DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG) /*! \qmlclass ParticleSystem QSGParticleSystem \inqmlmodule QtQuick.Particles 2 @@ -64,8 +66,23 @@ QT_BEGIN_NAMESPACE /*! \qmlproperty bool QtQuick.Particles2::ParticleSystem::running - If running is set to false, the particle system will not advance the simulation. + If running is set to false, the particle system will stop the simulation. All particles + will be destroyed when the system is set to running again. + + It can also be controlled with the start() and stop() methods. +*/ + + +/*! + \qmlproperty bool QtQuick.Particles2::ParticleSystem::paused + + If paused is set to true, the particle system will not advance the simulation. When + paused is set to false again, the simulation will resume from the same point it was + paused. + + It can also be controlled with the pause() and resume() methods. */ + /*! \qmlproperty int QtQuick.Particles2::ParticleSystem::startTime @@ -491,13 +508,10 @@ QSGParticleSystem::QSGParticleSystem(QSGItem *parent) : QSGItem(parent), m_particle_count(0), m_running(true) , m_startTime(0), m_nextIndex(0), m_componentComplete(false), m_spriteEngine(0) { - QSGParticleGroupData* gd = new QSGParticleGroupData(0, this);//Default group - m_groupData.insert(0,gd); - m_groupIds.insert("",0); - m_nextGroupId = 1; - connect(&m_painterMapper, SIGNAL(mapped(QObject*)), this, SLOT(loadPainter(QObject*))); + + m_debugMode = qmlParticlesDebug(); } QSGParticleSystem::~QSGParticleSystem() @@ -506,7 +520,22 @@ QSGParticleSystem::~QSGParticleSystem() delete gd; } -QDeclarativeListProperty QSGParticleSystem::particleStates() +void QSGParticleSystem::initGroups() +{ + m_reusableIndexes.clear(); + m_nextIndex = 0; + + qDeleteAll(m_groupData); + m_groupData.clear(); + m_groupIds.clear(); + + QSGParticleGroupData* gd = new QSGParticleGroupData(0, this);//Default group + m_groupData.insert(0,gd); + m_groupIds.insert("",0); + m_nextGroupId = 1; +} + + QDeclarativeListProperty QSGParticleSystem::particleStates() { return QDeclarativeListProperty(this, &m_states, spriteAppend, spriteCount, spriteAt, spriteClear); } @@ -514,11 +543,10 @@ QDeclarativeListProperty QSGParticleSystem::particleStates() void QSGParticleSystem::registerParticlePainter(QSGParticlePainter* p) { //TODO: a way to Unregister emitters, painters and affectors - m_particlePainters << QPointer(p);//###Set or uniqueness checking? + m_painters << 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) @@ -537,6 +565,120 @@ void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a) m_affectors << QPointer(a); } +void QSGParticleSystem::setRunning(bool arg) +{ + if (m_running != arg) { + m_running = arg; + emit runningChanged(arg); + setPaused(false); + if (m_animation)//Not created until componentCompleted + m_running ? m_animation->start() : m_animation->stop(); + reset(); + } +} + +void QSGParticleSystem::setPaused(bool arg){ + if (m_paused != arg) { + m_paused = arg; + if (m_animation && m_animation->state() != QAbstractAnimation::Stopped) + m_paused ? m_animation->pause() : m_animation->resume(); + if (!m_paused){ + foreach (QSGParticlePainter *p, m_painters) + p->update(); + } + emit pausedChanged(arg); + } +} + +void QSGParticleSystem::stateRedirect(QDeclarativeListProperty *prop, QObject *value) +{ + //Hooks up automatic state-associated stuff + QSGParticleSystem* sys = qobject_cast(prop->object->parent()); + QSGSprite* sprite = qobject_cast(prop->object); + if (!sprite || !sys) + return; + QStringList list; + list << sprite->name(); + QSGParticleAffector* a = qobject_cast(value); + if (a){ + a->setParentItem(sys); + a->setParticles(list); + a->setSystem(sys); + return; + } + QSGFollowEmitter* fe = qobject_cast(value); + if (fe){ + fe->setParentItem(sys); + fe->setFollow(sprite->name()); + fe->setSystem(sys); + return; + } + QSGParticleEmitter* e = qobject_cast(value); + if (e){ + e->setParentItem(sys); + e->setParticle(sprite->name()); + e->setSystem(sys); + return; + } + QSGParticlePainter* p = qobject_cast(value); + if (p){ + p->setParentItem(sys); + p->setParticles(list); + p->setSystem(sys); + return; + } + qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost."; +} + +void QSGParticleSystem::componentComplete() + +{ + QSGItem::componentComplete(); + m_componentComplete = true; + m_animation = new QSGParticleSystemAnimation(this); + reset();//restarts animation as well +} + +void QSGParticleSystem::reset() +{ + if (!m_componentComplete) + return; + + m_timeInt = 0; + //Clear guarded pointers which have been deleted + int cleared = 0; + cleared += m_emitters.removeAll(0); + cleared += m_painters.removeAll(0); + cleared += m_affectors.removeAll(0); + + m_bySysIdx.resize(0); + initGroups();//Also clears all logical particles + + if (!m_running) + return; + + foreach (QSGParticleEmitter* e, m_emitters) + e->reset(); + + emittersChanged(); + + foreach (QSGParticlePainter *p, m_painters){ + loadPainter(p); + p->reset(); + } + + //### Do affectors need reset too? + + if (m_animation){//reset restarts animation (if running) + m_animation->stop(); + m_animation->start(); + if (m_paused) + m_animation->pause(); + } + m_initialized = true; +} + + void QSGParticleSystem::loadPainter(QObject *p) { if (!m_componentComplete) @@ -566,7 +708,7 @@ void QSGParticleSystem::loadPainter(QObject *p) } } painter->setCount(particleCount); - painter->update();//###Initial update here? + painter->update();//Initial update here return; } @@ -609,101 +751,14 @@ void QSGParticleSystem::emittersChanged() Q_ASSERT(m_particle_count >= m_bySysIdx.size());//XXX when GC done right m_bySysIdx.resize(m_particle_count); - foreach (QSGParticlePainter *p, m_particlePainters) + foreach (QSGParticlePainter *p, m_painters) loadPainter(p); if (!m_states.isEmpty()) createEngine(); - if (m_particle_count > 16000)//###Investigate if these limits are worth warning about? - qWarning() << "Particle system arbitarily believes it has a vast number of particles (>16000). Expect poor performance"; -} - -void QSGParticleSystem::setRunning(bool arg) -{ - if (m_running != arg) { - m_running = arg; - emit runningChanged(arg); - reset(); - } -} - -void QSGParticleSystem::stateRedirect(QDeclarativeListProperty *prop, QObject *value) -{ - //Hooks up automatic state-associated stuff - QSGParticleSystem* sys = qobject_cast(prop->object->parent()); - QSGSprite* sprite = qobject_cast(prop->object); - if (!sprite || !sys) - return; - QStringList list; - list << sprite->name(); - QSGParticleAffector* a = qobject_cast(value); - if (a){ - a->setParentItem(sys); - a->setParticles(list); - a->setSystem(sys); - return; - } - QSGFollowEmitter* e = qobject_cast(value); - if (e){ - e->setParentItem(sys); - e->setFollow(sprite->name()); - e->setSystem(sys); - return; - } - QSGParticlePainter* p = qobject_cast(value); - if (p){ - p->setParentItem(sys); - p->setParticles(list); - p->setSystem(sys); - return; - } - qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost."; -} - -void QSGParticleSystem::componentComplete() - -{ - QSGItem::componentComplete(); - m_componentComplete = true; - m_animation = new QSGParticleSystemAnimation(this); - reset();//restarts animation as well -} - -void QSGParticleSystem::reset()//TODO: Needed? Or just in component complete? -{ - if (!m_componentComplete) - return; - - m_timeInt = 0; - //Clear guarded pointers which have been deleted - int cleared = 0; - cleared += m_emitters.removeAll(0); - cleared += m_particlePainters.removeAll(0); - cleared += m_affectors.removeAll(0); - - 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; - - foreach (QSGParticlePainter *p, m_particlePainters){ - loadPainter(p); - p->reset(); - } - - if (m_animation){ - m_animation->stop(); - m_animation->start(); - } - m_initialized = true; + if (m_debugMode) + qDebug() << "Particle system emitters changed. New particle count: " << m_particle_count; } void QSGParticleSystem::createEngine() diff --git a/src/declarative/particles/qsgparticlesystem_p.h b/src/declarative/particles/qsgparticlesystem_p.h index abb7f52c31..f578c43378 100644 --- a/src/declarative/particles/qsgparticlesystem_p.h +++ b/src/declarative/particles/qsgparticlesystem_p.h @@ -218,6 +218,7 @@ class QSGParticleSystem : public QSGItem { Q_OBJECT Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) Q_PROPERTY(QDeclarativeListProperty particleStates READ particleStates) @@ -247,10 +248,18 @@ signals: void startTimeChanged(int arg); + void pausedChanged(bool arg); + public slots: + void start(){setRunning(true);} + void stop(){setRunning(false);} + void restart(){setRunning(false);setRunning(true);} + void pause(){setPaused(true);} + void resume(){setPaused(false);} + void reset(); void setRunning(bool arg); - + void setPaused(bool arg); void setStartTime(int arg) { @@ -264,6 +273,7 @@ public slots: virtual int duration() const { return -1; } + protected: //This one only once per frame (effectively) void componentComplete(); @@ -300,12 +310,18 @@ public://###but only really for related class usage. Perhaps we should all be fr int m_particle_count; static void stateRedirect(QDeclarativeListProperty *prop, QObject *value);//From QSGSprite + bool isPaused() const + { + return m_paused; + } + private: void initializeSystem(); + void initGroups(); bool m_running; QList > m_emitters; QList > m_affectors; - QList > m_particlePainters; + QList > m_painters; QList > m_syncList; qint64 m_startTime; int m_nextGroupId; @@ -319,6 +335,8 @@ private: friend class QSGParticleSystemAnimation; void updateCurrentTime( int currentTime ); QSGParticleSystemAnimation* m_animation; + bool m_paused; + bool m_debugMode; }; // Internally, this animation drives all the timing. Painters sync up in their updatePaintNode -- cgit v1.2.3