Affectors now simulate with a minimum granularity
authorAlan Alpert <alan.alpert@nokia.com>
Thu, 20 Oct 2011 06:58:06 +0000 (16:58 +1000)
committerQt by Nokia <qt-info@nokia.com>
Mon, 24 Oct 2011 10:36:29 +0000 (12:36 +0200)
Since all time is sourced from the ParticleSystem, the easiest way is
to alter the common time point while doing the multiple simulation runs.

Also removes the experimental TargetAffector.

Change-Id: Ieb52f8990445fe95f94070175a0f9beb6686324a
Reviewed-by: Martin Jones <martin.jones@nokia.com>

examples/declarative/particles/affectors/attractor.qml
src/declarative/particles/particles.pri
src/declarative/particles/qsgcustomaffector.cpp
src/declarative/particles/qsggroupgoal.cpp
src/declarative/particles/qsgparticleaffector.cpp
src/declarative/particles/qsgparticleaffector_p.h
src/declarative/particles/qsgparticlesmodule.cpp
src/declarative/particles/qsgspritegoal.cpp
src/declarative/particles/qsgtargetaffector.cpp [deleted file]
src/declarative/particles/qsgtargetaffector_p.h [deleted file]

index d6660d6..c5bc4fd 100644 (file)
@@ -178,29 +178,31 @@ Rectangle {
             drag.axis: Drag.XandYAxis
             drag.target: ship
         }
-        Emitter {
-            group: "engine"
-            system: particles
-            emitRate: 200
-            lifeSpan: 1000
-            size: 10
-            endSize: 4
-            sizeVariation: 4
-            speed: PointDirection { x: -128; xVariation: 32 }
-            height: parent.height
-            width: 20
-        }
-        Emitter {
-            group: "shot"
-            system: particles
-            emitRate: 32
-            lifeSpan: 2000
-            enabled: spacePressed
-            size: 40
-            speed: PointDirection { x: 256; }
-            x: parent.width
-            y: parent.height/2
-        }
+    }
+    Emitter {
+        group: "engine"
+        system: particles
+        emitRate: 200
+        lifeSpan: 1000
+        size: 10
+        endSize: 4
+        sizeVariation: 4
+        speed: PointDirection { x: -128; xVariation: 32 }
+        height: ship.height
+        y: ship.y
+        x: ship.x
+        width: 20
+    }
+    Emitter {
+        group: "shot"
+        system: particles
+        emitRate: 32
+        lifeSpan: 2000
+        enabled: spacePressed
+        size: 40
+        speed: PointDirection { x: 256; }
+        x: ship.x + ship.width
+        y: ship.y + ship.height/2
     }
 
     Text {
index a3a8c90..4663c17 100644 (file)
@@ -24,7 +24,6 @@ HEADERS += \
     $$PWD/qsgtargetdirection_p.h \
     $$PWD/qsgturbulence_p.h \
     $$PWD/qsgwander_p.h \
-    $$PWD/qsgtargetaffector_p.h \
     $$PWD/qsgcumulativedirection_p.h \
     $$PWD/qsgv8particledata_p.h \
     $$PWD/qsgrectangleextruder_p.h \
@@ -58,7 +57,6 @@ SOURCES += \
     $$PWD/qsgtargetdirection.cpp \
     $$PWD/qsgturbulence.cpp \
     $$PWD/qsgwander.cpp \
-    $$PWD/qsgtargetaffector.cpp \
     $$PWD/qsgcumulativedirection.cpp \
     $$PWD/qsgv8particledata.cpp \
     $$PWD/qsgrectangleextruder.cpp \
index ffa86aa..28b220d 100644 (file)
@@ -87,13 +87,30 @@ void QSGCustomAffector::affectSystem(qreal dt)
                 if (shouldAffect(d))
                     toAffect << d;
 
+    if (toAffect.isEmpty())
+        return;
+
     v8::HandleScope handle_scope;
     v8::Context::Scope scope(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this))->context());
     v8::Handle<v8::Array> array = v8::Array::New(toAffect.size());
     for (int i=0; i<toAffect.size(); i++)
         array->Set(i, toAffect[i]->v8Value().toHandle());
 
-    emit affectParticles(QDeclarativeV8Handle::fromHandle(array), dt);
+    if (dt >= simulationCutoff || dt <= simulationDelta) {
+        emit affectParticles(QDeclarativeV8Handle::fromHandle(array), dt);
+    } else {
+        int realTime = m_system->timeInt;
+        m_system->timeInt -= dt * 1000.0;
+        while (dt > simulationDelta) {
+            m_system->timeInt += simulationDelta * 1000.0;
+            dt -= simulationDelta;
+            emit affectParticles(QDeclarativeV8Handle::fromHandle(array), simulationDelta);
+        }
+        m_system->timeInt = realTime;
+        if (dt > 0.0) {
+            emit affectParticles(QDeclarativeV8Handle::fromHandle(array), dt);
+        }
+    }
 
     foreach (QSGParticleData* d, toAffect)
         if (d->update == 1.0)
index 78fa786..b83c61f 100644 (file)
@@ -77,6 +77,7 @@ QT_BEGIN_NAMESPACE
 QSGGroupGoalAffector::QSGGroupGoalAffector(QQuickItem *parent) :
     QSGParticleAffector(parent), m_jump(false)
 {
+    m_ignoresTime = true;
 }
 
 void QSGGroupGoalAffector::setGoalState(QString arg)
index 2758e93..e31f8ff 100644 (file)
@@ -52,6 +52,16 @@ QT_BEGIN_NAMESPACE
     when a particle meets certain conditions.
 
     If an affector has a defined size, then it will only affect particles within its size and position on screen.
+
+    Affectors have different performance characteristics to the other particle system elements. In particular,
+    they have some simplifications to try to maintain a simulation at real-time or faster. When running a system
+    with Affectors, irregular frame timings that grow too large ( > one second per frame) will cause the Affectors
+    to try and cut corners with a faster but less accurate simulation. If the system has multiple affectors the order
+    in which they are applied is not guaranteed, and when simulating larger time shifts they will simulate the whole
+    shift each, which can lead to different results compared to smaller time shifts.
+
+    Accurate simulation for large numbers of particles (hundreds) with multiple affectors may be possible on some hardware,
+    but on less capable hardware you should expect small irregularties in the simulation as simulates with worse granularity.
 */
 /*!
     \qmlproperty ParticleSystem QtQuick.Particles2::Affector::system
@@ -127,7 +137,7 @@ QT_BEGIN_NAMESPACE
     x,y is the particles current position.
 */
 QSGParticleAffector::QSGParticleAffector(QQuickItem *parent) :
-    QQuickItem(parent), m_needsReset(false), m_system(0), m_enabled(true)
+    QQuickItem(parent), m_needsReset(false), m_ignoresTime(false), m_system(0), m_enabled(true)
   , m_updateIntSet(false), m_shape(new QSGParticleExtruder(this))
 {
 }
@@ -185,19 +195,40 @@ void QSGParticleAffector::postAffect(QSGParticleData* d)
         emit affected(d->curX(), d->curY());
 }
 
+const qreal QSGParticleAffector::simulationDelta = 0.020;
+const qreal QSGParticleAffector::simulationCutoff = 1.000;
+
 void QSGParticleAffector::affectSystem(qreal dt)
 {
     if (!m_enabled)
         return;
-    //If not reimplemented, calls affect particle per particle
+    //If not reimplemented, calls affectParticle per particle
     //But only on particles in targeted system/area
     updateOffsets();//### Needed if an ancestor is transformed.
-    foreach (QSGParticleGroupData* gd, m_system->groupData)
-        if (activeGroup(m_system->groupData.key(gd)))
-            foreach (QSGParticleData* d, gd->data)
-                if (shouldAffect(d))
-                    if (affectParticle(d, dt))
+    foreach (QSGParticleGroupData* gd, m_system->groupData) {
+        if (activeGroup(m_system->groupData.key(gd))) {
+            foreach (QSGParticleData* d, gd->data) {
+                if (shouldAffect(d)) {
+                    bool affected = false;
+                    qreal myDt = dt;
+                    if (!m_ignoresTime && myDt < simulationCutoff) {
+                        int realTime = m_system->timeInt;
+                        m_system->timeInt -= myDt * 1000.0;
+                        while (myDt > simulationDelta) {
+                            m_system->timeInt += simulationDelta * 1000.0;
+                            affected = affectParticle(d, simulationDelta) || affected;
+                            myDt -= simulationDelta;
+                        }
+                        m_system->timeInt = realTime;
+                    }
+                    if (myDt > 0.0)
+                        affected = affectParticle(d, myDt) || affected;
+                    if (affected)
                         postAffect(d);
+                }
+            }
+        }
+    }
 }
 
 bool QSGParticleAffector::affectParticle(QSGParticleData *, qreal )
index bc6864e..10c1cf0 100644 (file)
@@ -170,6 +170,7 @@ protected:
     friend class QSGParticleSystem;
     virtual bool affectParticle(QSGParticleData *d, qreal dt);
     bool m_needsReset;//### What is this really saving?
+    bool m_ignoresTime;
     QSGParticleSystem* m_system;
     QStringList m_groups;
     bool activeGroup(int g);
@@ -179,6 +180,8 @@ protected:
     virtual void componentComplete();
     QPointF m_offset;
     bool isAffectedConnected();
+    static const qreal simulationDelta;
+    static const qreal simulationCutoff;
 private:
     QSet<int> m_groupIds;
     QSet<QPair<int, int> > m_onceOffed;
index 9e73b82..6e170b1 100644 (file)
@@ -63,7 +63,6 @@
 #include "qsgtargetdirection_p.h"
 #include "qsgturbulence_p.h"
 #include "qsgwander_p.h"
-#include "qsgtargetaffector_p.h"
 #include "qsgcumulativedirection_p.h"
 #include "qsgcustomaffector_p.h"
 #include "qsgrectangleextruder_p.h"
@@ -106,7 +105,6 @@ void QSGParticlesModule::defineModule()
     qmlRegisterType<QSGSpriteGoalAffector>(uri, 2, 0, "SpriteGoal");
     qmlRegisterType<QSGGroupGoalAffector>(uri, 2, 0, "GroupGoal");
     qmlRegisterType<QSGTurbulenceAffector>(uri, 2, 0 , "Turbulence");
-    qmlRegisterType<QSGTargetAffector>(uri, 2, 0 , "Target");
     qmlRegisterType<QSGMoveAffector>(uri, 2, 0, "Move");
 
     //Exposed just for completeness
index 4765eb2..6d4c0a3 100644 (file)
@@ -87,6 +87,7 @@ QSGSpriteGoalAffector::QSGSpriteGoalAffector(QQuickItem *parent) :
     m_systemStates(false),
     m_notUsingEngine(false)
 {
+    m_ignoresTime = true;
 }
 
 void QSGSpriteGoalAffector::updateStateIndex(QQuickStochasticEngine* e)
diff --git a/src/declarative/particles/qsgtargetaffector.cpp b/src/declarative/particles/qsgtargetaffector.cpp
deleted file mode 100644 (file)
index 86f3250..0000000
+++ /dev/null
@@ -1,92 +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 "qsgtargetaffector_p.h"
-#include <QDebug>
-
-QSGTargetAffector::QSGTargetAffector(QQuickItem *parent) :
-    QSGParticleAffector(parent), m_targetX(0), m_targetY(0),
-    m_targetWidth(0), m_targetHeight(0), m_defaultShape(new QSGParticleExtruder(this)),
-    m_targetShape(m_defaultShape), m_targetTime(-1)
-{
-    m_needsReset = true;
-}
-
-void QSGTargetAffector::reset(QSGParticleData* d)
-{
-    QSGParticleAffector::reset(d);
-    m_targets[qMakePair<int,int>(d->group, d->index)] = m_targetShape->extrude(QRectF(m_targetX, m_targetY, m_targetWidth, m_targetHeight));
-}
-
-bool QSGTargetAffector::affectParticle(QSGParticleData *d, qreal dt)
-{
-    Q_UNUSED(dt);
-    QPointF target = m_targets[qMakePair<int,int>(d->group, d->index)];
-    if (target.isNull())
-        return false;
-    qreal tt = m_targetTime==-1?d->lifeSpan:(m_targetTime / 1000.0);
-    qreal t = tt - (d->lifeSpan - d->lifeLeft());
-    if (t <= 0)
-        return false;
-    qreal tx = d->x + d->vx * tt + 0.5 * d->ax * tt * tt;
-    qreal ty = d->y + d->vy * tt + 0.5 * d->ay * tt * tt;
-
-    if (QPointF(tx,ty) == target)
-        return false;
-
-    qreal vX = (target.x() - d->x) / tt;
-    qreal vY = (target.y() - d->y) / tt;
-
-    qreal w = 1 - (t / tt) + 0.05;
-    w = qMin<qreal>(w, 1.0);
-    qreal wvX = vX * w + d->vx * (1 - w);
-    qreal wvY = vY * w + d->vy * (1 - w);
-    //Screws with the acceleration so that the given start pos with the chosen weighted velocity will still end at the target coordinates
-    qreal ax = (2*(target.x() - d->x - wvX*tt)) / (tt*tt);
-    qreal ay = (2*(target.y() - d->y - wvY*tt)) / (tt*tt);
-
-    d->vx = wvX;
-    d->vy = wvY;
-    d->ax = ax;
-    d->ay = ay;
-
-    return true;
-}
diff --git a/src/declarative/particles/qsgtargetaffector_p.h b/src/declarative/particles/qsgtargetaffector_p.h
deleted file mode 100644 (file)
index 264ba30..0000000
+++ /dev/null
@@ -1,168 +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$
-**
-****************************************************************************/
-
-#ifndef QSGTARGETAFFECTOR_H
-#define QSGTARGETAFFECTOR_H
-
-#include "qsgparticleaffector_p.h"
-
-class QSGTargetAffector : public QSGParticleAffector
-{
-    Q_OBJECT
-    Q_PROPERTY(int targetX READ targetX WRITE setTargetX NOTIFY targetXChanged)
-    Q_PROPERTY(int targetY READ targetY WRITE setTargetY NOTIFY targetYChanged)
-    Q_PROPERTY(int targetWidth READ targetWidth WRITE setTargetWidth NOTIFY targetWidthChanged)
-    Q_PROPERTY(int targetHeight READ targetHeight WRITE setTargetHeight NOTIFY targetHeightChanged)
-    Q_PROPERTY(QSGParticleExtruder* targetShape READ targetShape WRITE setTargetShape NOTIFY targetShapeChanged)
-    Q_PROPERTY(int targetTime READ targetTime WRITE setTargetTime NOTIFY targetTimeChanged)
-
-public:
-    explicit QSGTargetAffector(QQuickItem *parent = 0);
-
-    int targetX() const
-    {
-        return m_targetX;
-    }
-
-    int targetY() const
-    {
-        return m_targetY;
-    }
-
-    int targetWidth() const
-    {
-        return m_targetWidth;
-    }
-
-    int targetHeight() const
-    {
-        return m_targetHeight;
-    }
-
-    QSGParticleExtruder* targetShape() const
-    {
-        return m_targetShape;
-    }
-
-    int targetTime() const
-    {
-        return m_targetTime;
-    }
-
-signals:
-
-    void targetXChanged(int arg);
-
-    void targetYChanged(int arg);
-
-    void targetWidthChanged(int arg);
-
-    void targetHeightChanged(int arg);
-
-    void targetShapeChanged(QSGParticleExtruder* arg);
-
-    void targetTimeChanged(int arg);
-
-public slots:
-    void setTargetX(int arg)
-    {
-        if (m_targetX != arg) {
-            m_targetX = arg;
-            emit targetXChanged(arg);
-        }
-    }
-
-    void setTargetY(int arg)
-    {
-        if (m_targetY != arg) {
-            m_targetY = arg;
-            emit targetYChanged(arg);
-        }
-    }
-
-    void setTargetWidth(int arg)
-    {
-        if (m_targetWidth != arg) {
-            m_targetWidth = arg;
-            emit targetWidthChanged(arg);
-        }
-    }
-
-    void setTargetHeight(int arg)
-    {
-        if (m_targetHeight != arg) {
-            m_targetHeight = arg;
-            emit targetHeightChanged(arg);
-        }
-    }
-
-    void setTargetShape(QSGParticleExtruder* arg)
-    {
-        if (m_targetShape != arg) {
-            m_targetShape = arg;
-            emit targetShapeChanged(arg);
-        }
-    }
-
-    void setTargetTime(int arg)
-    {
-        if (m_targetTime != arg) {
-            m_targetTime = arg;
-            emit targetTimeChanged(arg);
-        }
-    }
-
-protected:
-    virtual void reset(QSGParticleData*);
-    virtual bool affectParticle(QSGParticleData *d, qreal dt);
-private:
-    int m_targetX;
-    int m_targetY;
-    int m_targetWidth;
-    int m_targetHeight;
-    QSGParticleExtruder* m_defaultShape;
-    QSGParticleExtruder* m_targetShape;
-    int m_targetTime;
-
-    QHash<QPair<int, int>, QPointF> m_targets;
-};
-
-#endif // QSGTARGETAFFECTOR_H