diff options
Diffstat (limited to 'src/declarative/particles/qquickparticlesystem.cpp')
-rw-r--r-- | src/declarative/particles/qquickparticlesystem.cpp | 1106 |
1 files changed, 0 insertions, 1106 deletions
diff --git a/src/declarative/particles/qquickparticlesystem.cpp b/src/declarative/particles/qquickparticlesystem.cpp deleted file mode 100644 index 9e6bafab20..0000000000 --- a/src/declarative/particles/qquickparticlesystem.cpp +++ /dev/null @@ -1,1106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickparticlesystem_p.h" -#include <qsgnode.h> -#include "qquickparticleemitter_p.h" -#include "qquickparticleaffector_p.h" -#include "qquickparticlepainter_p.h" -#include <private/qquickspriteengine_p.h> -#include <private/qquicksprite_p.h> -#include "qquickv8particledata_p.h" -#include "qquickparticlegroup_p.h" - -#include "qquicktrailemitter_p.h"//###For auto-follow on states, perhaps should be in emitter? -#include <private/qdeclarativeengine_p.h> -#include <cmath> -#include <QDebug> - -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 QQuickParticleSystem - \inqmlmodule QtQuick.Particles 2 - \brief The ParticleSystem brings together ParticlePainter, Emitter and Affector elements. - -*/ - -/*! - \qmlproperty bool QtQuick.Particles2::ParticleSystem::running - - 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. - - The simulation will automatically pause if it detects that there are no live particles - left, and unpause when new live particles are added. - - It can also be controlled with the pause() and resume() methods. -*/ - -/*! - \qmlproperty bool QtQuick.Particles2::ParticleSystem::empty - - empty is set to true when there are no live particles left in the system. - - You can use this to pause the system, keeping it from spending any time updating, - but you will need to resume it in order for additional particles to be generated - by the system. - - To kill all the particles in the system, use a Kill affector. -*/ - -/*! - \qmlproperty list<Sprite> QtQuick.Particles2::ParticleSystem::particleStates - - You can define a sub-set of particle groups in this property in order to provide them - with stochastic state transitions. - - Each QtQuick2::Sprite in this list is interpreted as corresponding to the particle group - with ths same name. Any transitions defined in these sprites will take effect on the particle - groups as well. Additionally TrailEmitters, Affectors and ParticlePainters definined - inside one of these sprites are automatically associated with the corresponding particle group. -*/ - -/*! - \qmlmethod void QtQuick.Particles2::ParticleSystem::pause - - Pauses the simulation if it is running. - - \sa resume, paused -*/ - -/*! - \qmlmethod void QtQuick.Particles2::ParticleSystem::resume - - Resumes the simulation if it is paused. - - \sa pause, paused -*/ - -/*! - \qmlmethod void QtQuick.Particles2::ParticleSystem::start - - Starts the simulation if it has not already running. - - \sa stop, restart, running -*/ - -/*! - \qmlmethod void QtQuick.Particles2::ParticleSystem::stop - - Stops the simulation if it is running. - - \sa start, restart, running -*/ - -/*! - \qmlmethod void QtQuick.Particles2::ParticleSystem::restart - - Stops the simulation if it is running, and then starts it. - - \sa stop, restart, running -*/ -/*! - \qmlmethod void QtQuick.Particles2::ParticleSystem::reset - - Discards all currently existing particles. - -*/ -const qreal EPSILON = 0.001; -//Utility functions for when within 1ms is close enough -bool timeEqualOrGreater(qreal a, qreal b) -{ - return (a+EPSILON >= b); -} - -bool timeLess(qreal a, qreal b) -{ - return (a-EPSILON < b); -} - -bool timeEqual(qreal a, qreal b) -{ - return (a+EPSILON > b) && (a-EPSILON < b); -} - -int roundedTime(qreal a) -{// in ms - return (int)qRound(a*1000.0); -} - -QQuickParticleDataHeap::QQuickParticleDataHeap() - : m_data(0) -{ - m_data.reserve(1000); - clear(); -} - -void QQuickParticleDataHeap::grow() //###Consider automatic growth vs resize() calls from GroupData -{ - m_data.resize(1 << ++m_size); -} - -void QQuickParticleDataHeap::insert(QQuickParticleData* data) -{ - insertTimed(data, roundedTime(data->t + data->lifeSpan)); -} - -void QQuickParticleDataHeap::insertTimed(QQuickParticleData* data, int time) -{ - //TODO: Optimize 0 lifespan (or already dead) case - if (m_lookups.contains(time)) { - m_data[m_lookups[time]].data << data; - return; - } - if (m_end == (1 << m_size)) - grow(); - m_data[m_end].time = time; - m_data[m_end].data.clear(); - m_data[m_end].data.insert(data); - m_lookups.insert(time, m_end); - bubbleUp(m_end++); -} - -int QQuickParticleDataHeap::top() -{ - if (m_end == 0) - return 1 << 30; - return m_data[0].time; -} - -QSet<QQuickParticleData*> QQuickParticleDataHeap::pop() -{ - if (!m_end) - return QSet<QQuickParticleData*> (); - QSet<QQuickParticleData*> ret = m_data[0].data; - m_lookups.remove(m_data[0].time); - if (m_end == 1) { - --m_end; - } else { - m_data[0] = m_data[--m_end]; - bubbleDown(0); - } - return ret; -} - -void QQuickParticleDataHeap::clear() -{ - m_size = 0; - m_end = 0; - //m_size is in powers of two. So to start at 0 we have one allocated - m_data.resize(1); - m_lookups.clear(); -} - -bool QQuickParticleDataHeap::contains(QQuickParticleData* d) -{ - for (int i=0; i<m_end; i++) - if (m_data[i].data.contains(d)) - return true; - return false; -} - -void QQuickParticleDataHeap::swap(int a, int b) -{ - m_tmp = m_data[a]; - m_data[a] = m_data[b]; - m_data[b] = m_tmp; - m_lookups[m_data[a].time] = a; - m_lookups[m_data[b].time] = b; -} - -void QQuickParticleDataHeap::bubbleUp(int idx)//tends to be called once -{ - if (!idx) - return; - int parent = (idx-1)/2; - if (m_data[idx].time < m_data[parent].time) { - swap(idx, parent); - bubbleUp(parent); - } -} - -void QQuickParticleDataHeap::bubbleDown(int idx)//tends to be called log n times -{ - int left = idx*2 + 1; - if (left >= m_end) - return; - int lesser = left; - int right = idx*2 + 2; - if (right < m_end) { - if (m_data[left].time > m_data[right].time) - lesser = right; - } - if (m_data[idx].time > m_data[lesser].time) { - swap(idx, lesser); - bubbleDown(lesser); - } -} - -QQuickParticleGroupData::QQuickParticleGroupData(int id, QQuickParticleSystem* sys):index(id),m_size(0),m_system(sys) -{ - initList(); -} - -QQuickParticleGroupData::~QQuickParticleGroupData() -{ - foreach (QQuickParticleData* d, data) - delete d; -} - -int QQuickParticleGroupData::size() -{ - return m_size; -} - -QString QQuickParticleGroupData::name()//### Worth caching as well? -{ - return m_system->groupIds.key(index); -} - -void QQuickParticleGroupData::setSize(int newSize) -{ - if (newSize == m_size) - return; - Q_ASSERT(newSize > m_size);//XXX allow shrinking - data.resize(newSize); - for (int i=m_size; i<newSize; i++) { - data[i] = new QQuickParticleData(m_system); - data[i]->group = index; - data[i]->index = i; - reusableIndexes << i; - } - int delta = newSize - m_size; - m_size = newSize; - foreach (QQuickParticlePainter* p, painters) - p->setCount(p->count() + delta); -} - -void QQuickParticleGroupData::initList() -{ - dataHeap.clear(); -} - -void QQuickParticleGroupData::kill(QQuickParticleData* d) -{ - Q_ASSERT(d->group == index); - d->lifeSpan = 0;//Kill off - foreach (QQuickParticlePainter* p, painters) - p->reload(d); - reusableIndexes << d->index; -} - -QQuickParticleData* QQuickParticleGroupData::newDatum(bool respectsLimits) -{ - //recycle();//Extra recycler round to be sure? - - while (!reusableIndexes.empty()) { - int idx = *(reusableIndexes.begin()); - reusableIndexes.remove(idx); - if (data[idx]->stillAlive()) {// ### This means resurrection of 'dead' particles. Is that allowed? - prepareRecycler(data[idx]); - continue; - } - return data[idx]; - } - if (respectsLimits) - return 0; - - int oldSize = m_size; - setSize(oldSize + 10);//###+1,10%,+10? Choose something non-arbitrarily - reusableIndexes.remove(oldSize); - return data[oldSize]; -} - -bool QQuickParticleGroupData::recycle() -{ - while (dataHeap.top() <= m_system->timeInt) { - foreach (QQuickParticleData* datum, dataHeap.pop()) { - if (!datum->stillAlive()) { - reusableIndexes << datum->index; - } else { - prepareRecycler(datum); //ttl has been altered mid-way, put it back - } - } - } - - //TODO: If the data is clear, gc (consider shrinking stack size)? - return reusableIndexes.count() == m_size; -} - -void QQuickParticleGroupData::prepareRecycler(QQuickParticleData* d) -{ - if (d->lifeSpan*1000 < m_system->maxLife) { - dataHeap.insert(d); - } else { - while ((roundedTime(d->t) + 2*m_system->maxLife/3) <= m_system->timeInt) - d->extendLife(m_system->maxLife/3000.0); - dataHeap.insertTimed(d, roundedTime(d->t) + 2*m_system->maxLife/3); - } -} - -QQuickParticleData::QQuickParticleData(QQuickParticleSystem* sys) - : group(0) - , e(0) - , system(sys) - , index(0) - , systemIndex(-1) - , colorOwner(0) - , rotationOwner(0) - , deformationOwner(0) - , animationOwner(0) - , v8Datum(0) -{ - x = 0; - y = 0; - t = -1; - lifeSpan = 0; - size = 0; - endSize = 0; - vx = 0; - vy = 0; - ax = 0; - ay = 0; - xx = 1; - xy = 0; - yx = 0; - yy = 1; - rotation = 0; - rotationSpeed = 0; - autoRotate = 0; - animIdx = 0; - frameDuration = 1; - frameCount = 1; - animT = -1; - animX = 0; - animY = 0; - animWidth = 1; - animHeight = 1; - color.r = 255; - color.g = 255; - color.b = 255; - color.a = 255; - r = 0; - delegate = 0; - modelIndex = -1; -} - -QQuickParticleData::~QQuickParticleData() -{ - delete v8Datum; -} - -void QQuickParticleData::clone(const QQuickParticleData& other) -{ - x = other.x; - y = other.y; - t = other.t; - lifeSpan = other.lifeSpan; - size = other.size; - endSize = other.endSize; - vx = other.vx; - vy = other.vy; - ax = other.ax; - ay = other.ay; - xx = other.xx; - xy = other.xy; - yx = other.yx; - yy = other.yy; - rotation = other.rotation; - rotationSpeed = other.rotationSpeed; - autoRotate = other.autoRotate; - animIdx = other.animIdx; - frameDuration = other.frameDuration; - frameCount = other.frameCount; - animT = other.animT; - animX = other.animX; - animY = other.animY; - animWidth = other.animWidth; - animHeight = other.animHeight; - color.r = other.color.r; - color.g = other.color.g; - color.b = other.color.b; - color.a = other.color.a; - r = other.r; - delegate = other.delegate; - modelIndex = other.modelIndex; - - colorOwner = other.colorOwner; - rotationOwner = other.rotationOwner; - deformationOwner = other.deformationOwner; - animationOwner = other.animationOwner; -} - -QDeclarativeV8Handle QQuickParticleData::v8Value() -{ - if (!v8Datum) - v8Datum = new QQuickV8ParticleData(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(system)), this); - return v8Datum->v8Value(); -} -//sets the x accleration without affecting the instantaneous x velocity or position -void QQuickParticleData::setInstantaneousAX(qreal ax) -{ - qreal t = (system->timeInt / 1000.0) - this->t; - qreal vx = (this->vx + t*this->ax) - t*ax; - qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t; - qreal x = ex - t*vx - 0.5 * t*t*ax; - - this->ax = ax; - this->vx = vx; - this->x = x; -} - -//sets the x velocity without affecting the instantaneous x postion -void QQuickParticleData::setInstantaneousVX(qreal vx) -{ - qreal t = (system->timeInt / 1000.0) - this->t; - qreal evx = vx - t*this->ax; - qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t; - qreal x = ex - t*evx - 0.5 * t*t*this->ax; - - this->vx = evx; - this->x = x; -} - -//sets the instantaneous x postion -void QQuickParticleData::setInstantaneousX(qreal x) -{ - qreal t = (system->timeInt / 1000.0) - this->t; - this->x = x - t*this->vx - 0.5 * t*t*this->ax; -} - -//sets the y accleration without affecting the instantaneous y velocity or position -void QQuickParticleData::setInstantaneousAY(qreal ay) -{ - qreal t = (system->timeInt / 1000.0) - this->t; - qreal vy = (this->vy + t*this->ay) - t*ay; - qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t; - qreal y = ey - t*vy - 0.5 * t*t*ay; - - this->ay = ay; - this->vy = vy; - this->y = y; -} - -//sets the y velocity without affecting the instantaneous y position -void QQuickParticleData::setInstantaneousVY(qreal vy) -{ - qreal t = (system->timeInt / 1000.0) - this->t; - qreal evy = vy - t*this->ay; - qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t; - qreal y = ey - t*evy - 0.5 * t*t*this->ay; - - this->vy = evy; - this->y = y; -} - -//sets the instantaneous Y position -void QQuickParticleData::setInstantaneousY(qreal y) -{ - qreal t = (system->timeInt / 1000.0) - this->t; - this->y = y - t*this->vy - 0.5 * t*t*this->ay; -} - -qreal QQuickParticleData::curX() const -{ - qreal t = (system->timeInt / 1000.0) - this->t; - return this->x + this->vx * t + 0.5 * this->ax * t * t; -} - -qreal QQuickParticleData::curVX() const -{ - qreal t = (system->timeInt / 1000.0) - this->t; - return this->vx + t*this->ax; -} - -qreal QQuickParticleData::curY() const -{ - qreal t = (system->timeInt / 1000.0) - this->t; - return y + vy * t + 0.5 * ay * t * t; -} - -qreal QQuickParticleData::curVY() const -{ - qreal t = (system->timeInt / 1000.0) - this->t; - return vy + t*ay; -} - -void QQuickParticleData::debugDump() -{ - qDebug() << "Particle" << systemIndex << group << "/" << index << stillAlive() - << "Pos: " << x << "," << y - << "Vel: " << vx << "," << vy - << "Acc: " << ax << "," << ay - << "Size: " << size << "," << endSize - << "Time: " << t << "," <<lifeSpan << ";" << (system->timeInt / 1000.0) ; -} - -bool QQuickParticleData::stillAlive() -{ - if (!system) - return false; - return (t + lifeSpan - EPSILON) > ((qreal)system->timeInt/1000.0); -} - -bool QQuickParticleData::alive() -{ - if (!system) - return false; - qreal st = ((qreal)system->timeInt/1000.0); - return (t + EPSILON) < st && (t + lifeSpan - EPSILON) > st; -} - -float QQuickParticleData::curSize() -{ - if (!system || !lifeSpan) - return 0.0f; - return size + (endSize - size) * (1 - (lifeLeft() / lifeSpan)); -} - -float QQuickParticleData::lifeLeft() -{ - if (!system) - return 0.0f; - return (t + lifeSpan) - (system->timeInt/1000.0); -} - -void QQuickParticleData::extendLife(float time) -{ - qreal newX = curX(); - qreal newY = curY(); - qreal newVX = curVX(); - qreal newVY = curVY(); - - t += time; - animT += time; - - qreal elapsed = (system->timeInt / 1000.0) - t; - qreal evy = newVY - elapsed*ay; - qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay; - qreal evx = newVX - elapsed*ax; - qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax; - - x = ex; - vx = evx; - y = ey; - vy = evy; -} - -QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) : - QQuickItem(parent), - stateEngine(0), - m_running(true), - particleCount(0), - m_nextIndex(0), - m_componentComplete(false), - m_paused(false) -{ - connect(&m_painterMapper, SIGNAL(mapped(QObject*)), - this, SLOT(loadPainter(QObject*))); - - m_debugMode = qmlParticlesDebug(); -} - -QQuickParticleSystem::~QQuickParticleSystem() -{ - foreach (QQuickParticleGroupData* gd, groupData) - delete gd; -} - -void QQuickParticleSystem::initGroups() -{ - m_reusableIndexes.clear(); - m_nextIndex = 0; - - qDeleteAll(groupData); - groupData.clear(); - groupIds.clear(); - - QQuickParticleGroupData* gd = new QQuickParticleGroupData(0, this);//Default group - groupData.insert(0,gd); - groupIds.insert(QString(), 0); - m_nextGroupId = 1; -} - -void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p) -{ - //TODO: a way to Unregister emitters, painters and affectors - m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking? - connect(p, SIGNAL(groupsChanged(QStringList)), - &m_painterMapper, SLOT(map())); - loadPainter(p); -} - -void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e) -{ - m_emitters << QPointer<QQuickParticleEmitter>(e);//###How to get them out? - connect(e, SIGNAL(particleCountChanged()), - this, SLOT(emittersChanged())); - connect(e, SIGNAL(groupChanged(QString)), - this, SLOT(emittersChanged())); - emittersChanged(); - e->reset();//Start, so that starttime factors appropriately -} - -void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a) -{ - m_affectors << QPointer<QQuickParticleAffector>(a); -} - -void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g) -{ - m_groups << QPointer<QQuickParticleGroup>(g); - createEngine(); -} - -void QQuickParticleSystem::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 QQuickParticleSystem::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 (QQuickParticlePainter *p, m_painters) - p->update(); - } - emit pausedChanged(arg); - } -} - -void QQuickParticleSystem::statePropertyRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value) -{ - //Hooks up automatic state-associated stuff - QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent()); - QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object); - if (!group || !sys || !value) - return; - stateRedirect(group, sys, value); -} - -void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value) -{ - QStringList list; - list << group->name(); - QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value); - if (a) { - a->setParentItem(sys); - a->setGroups(list); - a->setSystem(sys); - return; - } - QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value); - if (fe) { - fe->setParentItem(sys); - fe->setFollow(group->name()); - fe->setSystem(sys); - return; - } - QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value); - if (e) { - e->setParentItem(sys); - e->setGroup(group->name()); - e->setSystem(sys); - return; - } - QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value); - if (p) { - p->setParentItem(sys); - p->setGroups(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 QQuickParticleSystem::componentComplete() - -{ - QQuickItem::componentComplete(); - m_componentComplete = true; - m_animation = new QQuickParticleSystemAnimation(this); - reset();//restarts animation as well -} - -void QQuickParticleSystem::reset() -{ - if (!m_componentComplete) - return; - - 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); - - bySysIdx.resize(0); - initGroups();//Also clears all logical particles - - if (!m_running) - return; - - foreach (QQuickParticleEmitter* e, m_emitters) - e->reset(); - - emittersChanged(); - - foreach (QQuickParticlePainter *p, m_painters) { - loadPainter(p); - p->reset(); - } - - //### Do affectors need reset too? - if (m_animation) {//Animation is explicitly disabled in benchmarks - //reset restarts animation (if running) - if ((m_animation->state() == QAbstractAnimation::Running)) - m_animation->stop(); - m_animation->start(); - if (m_paused) - m_animation->pause(); - } - - initialized = true; -} - - -void QQuickParticleSystem::loadPainter(QObject *p) -{ - if (!m_componentComplete) - return; - - QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p); - Q_ASSERT(painter);//XXX - foreach (QQuickParticleGroupData* sg, groupData) - sg->painters.remove(painter); - int particleCount = 0; - if (painter->groups().isEmpty()) {//Uses default particle - QStringList def; - def << QString(); - painter->setGroups(def); - particleCount += groupData[0]->size(); - groupData[0]->painters << painter; - } else { - foreach (const QString &group, painter->groups()) { - if (group != QLatin1String("") && !groupIds[group]) {//new group - int id = m_nextGroupId++; - QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this); - groupIds.insert(group, id); - groupData.insert(id, gd); - } - particleCount += groupData[groupIds[group]]->size(); - groupData[groupIds[group]]->painters << painter; - } - } - painter->setCount(particleCount); - painter->update();//Initial update here - return; -} - -void QQuickParticleSystem::emittersChanged() -{ - if (!m_componentComplete) - return; - - m_emitters.removeAll(0); - - - QList<int> previousSizes; - QList<int> newSizes; - for (int i=0; i<m_nextGroupId; i++) { - previousSizes << groupData[i]->size(); - newSizes << 0; - } - - foreach (QQuickParticleEmitter* e, m_emitters) {//Populate groups and set sizes. - if (!groupIds.contains(e->group()) - || (!e->group().isEmpty() && !groupIds[e->group()])) {//or it was accidentally inserted by a failed lookup earlier - int id = m_nextGroupId++; - QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this); - groupIds.insert(e->group(), id); - groupData.insert(id, gd); - previousSizes << 0; - newSizes << 0; - } - newSizes[groupIds[e->group()]] += e->particleCount(); - //###: Cull emptied groups? - } - - //TODO: Garbage collection? - particleCount = 0; - for (int i=0; i<m_nextGroupId; i++) { - groupData[i]->setSize(qMax(newSizes[i], previousSizes[i])); - particleCount += groupData[i]->size(); - } - - if (m_debugMode) - qDebug() << "Particle system emitters changed. New particle count: " << particleCount; - - if (particleCount > bySysIdx.size())//New datum requests haven't updated it - bySysIdx.resize(particleCount); - - foreach (QQuickParticlePainter *p, m_painters) - loadPainter(p); - - if (!m_groups.isEmpty()) - createEngine(); - -} - -void QQuickParticleSystem::createEngine() -{ - if (!m_componentComplete) - return; - if (stateEngine && m_debugMode) - qDebug() << "Resetting Existing Sprite Engine..."; - //### Solve the losses if size/states go down - foreach (QQuickParticleGroup* group, m_groups) { - bool exists = false; - foreach (const QString &name, groupIds.keys()) - if (group->name() == name) - exists = true; - if (!exists) { - int id = m_nextGroupId++; - QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this); - groupIds.insert(group->name(), id); - groupData.insert(id, gd); - } - } - - if (m_groups.count()) { - //Reorder groups List so as to have the same order as groupData - QList<QQuickParticleGroup*> newList; - for (int i=0; i<m_nextGroupId; i++) { - bool exists = false; - QString name = groupData[i]->name(); - foreach (QQuickParticleGroup* existing, m_groups) { - if (existing->name() == name) { - newList << existing; - exists = true; - } - } - if (!exists) { - newList << new QQuickParticleGroup(this); - newList.back()->setName(name); - } - } - m_groups = newList; - QList<QQuickStochasticState*> states; - foreach (QQuickParticleGroup* g, m_groups) - states << (QQuickStochasticState*)g; - - if (!stateEngine) - stateEngine = new QQuickStochasticEngine(this); - stateEngine->setCount(particleCount); - stateEngine->m_states = states; - - connect(stateEngine, SIGNAL(stateChanged(int)), - this, SLOT(particleStateChange(int))); - - } else { - if (stateEngine) - delete stateEngine; - stateEngine = 0; - } - -} - -void QQuickParticleSystem::particleStateChange(int idx) -{ - moveGroups(bySysIdx[idx], stateEngine->curState(idx)); -} - -void QQuickParticleSystem::moveGroups(QQuickParticleData *d, int newGIdx) -{ - if (!d || newGIdx == d->group) - return; - - QQuickParticleData* pd = newDatum(newGIdx, false, d->systemIndex); - if (!pd) - return; - - pd->clone(*d); - finishNewDatum(pd); - - d->systemIndex = -1; - groupData[d->group]->kill(d); -} - -int QQuickParticleSystem::nextSystemIndex() -{ - if (!m_reusableIndexes.isEmpty()) { - int ret = *(m_reusableIndexes.begin()); - m_reusableIndexes.remove(ret); - return ret; - } - if (m_nextIndex >= bySysIdx.size()) { - bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily - if (stateEngine) - stateEngine->setCount(bySysIdx.size()); - - } - return m_nextIndex++; -} - -QQuickParticleData* QQuickParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex) -{ - Q_ASSERT(groupId < groupData.count());//XXX shouldn't really be an assert - - QQuickParticleData* ret = groupData[groupId]->newDatum(respectLimits); - if (!ret) { - return 0; - } - if (sysIndex == -1) { - if (ret->systemIndex == -1) - ret->systemIndex = nextSystemIndex(); - } else { - if (ret->systemIndex != -1) { - if (stateEngine) - stateEngine->stop(ret->systemIndex); - m_reusableIndexes << ret->systemIndex; - bySysIdx[ret->systemIndex] = 0; - } - ret->systemIndex = sysIndex; - } - bySysIdx[ret->systemIndex] = ret; - - if (stateEngine) - stateEngine->start(ret->systemIndex, ret->group); - - m_empty = false; - return ret; -} - -void QQuickParticleSystem::emitParticle(QQuickParticleData* pd) -{// called from prepareNextFrame()->emitWindow - enforce? - //Account for relative emitter position - QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0)); - if (!offset.isNull()) { - pd->x += offset.x(); - pd->y += offset.y(); - } - - finishNewDatum(pd); -} - -void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd) -{ - Q_ASSERT(pd); - groupData[pd->group]->prepareRecycler(pd); - - foreach (QQuickParticleAffector *a, m_affectors) - if (a && a->m_needsReset) - a->reset(pd); - foreach (QQuickParticlePainter* p, groupData[pd->group]->painters) - if (p) - p->load(pd); -} - -void QQuickParticleSystem::updateCurrentTime( int currentTime ) -{ - if (!initialized) - return;//error in initialization - - //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time. - qreal dt = timeInt / 1000.; - timeInt = currentTime; - qreal time = timeInt / 1000.; - dt = time - dt; - needsReset.clear(); - - m_emitters.removeAll(0); - m_painters.removeAll(0); - m_affectors.removeAll(0); - - bool oldClear = m_empty; - m_empty = true; - foreach (QQuickParticleGroupData* gd, groupData)//Recycle all groups and see if they're out of live particles - m_empty = gd->recycle() && m_empty; - - if (stateEngine) - stateEngine->updateSprites(timeInt); - - foreach (QQuickParticleEmitter* emitter, m_emitters) - emitter->emitWindow(timeInt); - foreach (QQuickParticleAffector* a, m_affectors) - a->affectSystem(dt); - foreach (QQuickParticleData* d, needsReset) - foreach (QQuickParticlePainter* p, groupData[d->group]->painters) - p->reload(d); - - if (oldClear != m_empty) - emptyChanged(m_empty); -} - -int QQuickParticleSystem::systemSync(QQuickParticlePainter* p) -{ - if (!m_running) - return 0; - if (!initialized) - return 0;//error in initialization - p->performPendingCommits(); - return timeInt; -} - - -QT_END_NAMESPACE |