Initial CustomEmitter and CustomAffector implementation
authorAlan Alpert <alan.alpert@nokia.com>
Wed, 20 Jul 2011 03:46:52 +0000 (13:46 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 28 Jul 2011 09:27:26 +0000 (11:27 +0200)
Currently basic and can't access all properties.

Change-Id: I66efc235112cffcf1a3ca5cc69099a5d09ec7691
Reviewed-on: http://codereview.qt.nokia.com/1950
Reviewed-by: Martin Jones <martin.jones@nokia.com>
23 files changed:
examples/declarative/particles/custom/content/backgroundLeaves.jpg [new file with mode: 0755]
examples/declarative/particles/custom/content/realLeaf1.png [new file with mode: 0644]
examples/declarative/particles/custom/content/realLeaf2.png [new file with mode: 0644]
examples/declarative/particles/custom/content/realLeaf3.png [new file with mode: 0644]
examples/declarative/particles/custom/content/realLeaf4.png [new file with mode: 0644]
examples/declarative/particles/custom/custom.qml [new file with mode: 0644]
examples/declarative/particles/custom/fallingleaves.qml [new file with mode: 0644]
examples/declarative/particles/exampleslauncher.qml
examples/declarative/particles/launcherContent/icons/custom.png [new file with mode: 0644]
examples/declarative/particles/launcherContent/icons/fallingleaves.png [new file with mode: 0644]
src/declarative/particles/particles.pri
src/declarative/particles/qsgcustomaffector.cpp [new file with mode: 0644]
src/declarative/particles/qsgcustomaffector_p.h [new file with mode: 0644]
src/declarative/particles/qsgcustomemitter.cpp [new file with mode: 0644]
src/declarative/particles/qsgcustomemitter_p.h [new file with mode: 0644]
src/declarative/particles/qsgemitter_p.h
src/declarative/particles/qsgparticlesmodule.cpp
src/declarative/particles/qsgparticlesystem.cpp
src/declarative/particles/qsgparticlesystem_p.h
src/declarative/particles/qsgv8particledata.cpp [new file with mode: 0644]
src/declarative/particles/qsgv8particledata_p.h [new file with mode: 0644]
src/declarative/qml/v8/qv8engine.cpp
src/declarative/qml/v8/qv8engine_p.h

diff --git a/examples/declarative/particles/custom/content/backgroundLeaves.jpg b/examples/declarative/particles/custom/content/backgroundLeaves.jpg
new file mode 100755 (executable)
index 0000000..08be167
Binary files /dev/null and b/examples/declarative/particles/custom/content/backgroundLeaves.jpg differ
diff --git a/examples/declarative/particles/custom/content/realLeaf1.png b/examples/declarative/particles/custom/content/realLeaf1.png
new file mode 100644 (file)
index 0000000..1ec184a
Binary files /dev/null and b/examples/declarative/particles/custom/content/realLeaf1.png differ
diff --git a/examples/declarative/particles/custom/content/realLeaf2.png b/examples/declarative/particles/custom/content/realLeaf2.png
new file mode 100644 (file)
index 0000000..8ab84cd
Binary files /dev/null and b/examples/declarative/particles/custom/content/realLeaf2.png differ
diff --git a/examples/declarative/particles/custom/content/realLeaf3.png b/examples/declarative/particles/custom/content/realLeaf3.png
new file mode 100644 (file)
index 0000000..f1f3ea9
Binary files /dev/null and b/examples/declarative/particles/custom/content/realLeaf3.png differ
diff --git a/examples/declarative/particles/custom/content/realLeaf4.png b/examples/declarative/particles/custom/content/realLeaf4.png
new file mode 100644 (file)
index 0000000..51bde09
Binary files /dev/null and b/examples/declarative/particles/custom/content/realLeaf4.png differ
diff --git a/examples/declarative/particles/custom/custom.qml b/examples/declarative/particles/custom/custom.qml
new file mode 100644 (file)
index 0000000..3938705
--- /dev/null
@@ -0,0 +1,100 @@
+import QtQuick 2.0
+import QtQuick.Particles 2.0
+
+ParticleSystem{
+    id: sys
+    width: 360
+    height: 600
+    Rectangle{
+        z: -1
+        anchors.fill: parent
+        color: "black"
+        Text{
+            anchors.bottom: parent.bottom
+            anchors.horizontalCenter: parent.horizontalCenter
+            font.pixelSize: 42
+            color: "white"
+            text: "It's all in QML."
+        }
+    }
+    property real petalLength: 180
+    property real petalRotation: 0
+    NumberAnimation on petalRotation{
+        from: 0;
+        to: 360;
+        loops: -1;
+        running: true
+        duration: 24000
+    }
+    function convert(a){return a*(Math.PI/180);}
+    CustomEmitter{
+        lifeSpan: 4000
+        emitRate: 120
+        size: 12
+        anchors.centerIn: parent
+        onEmitParticle:{
+            particle.size = Math.max(12,Math.min(492,Math.tan(particle.t/2)*24));
+            var theta = Math.floor(Math.random() * 6.0) / 6.0;
+            theta *= 2.0*Math.PI;
+            theta += sys.convert(sys.petalRotation);
+            particle.sx = petalLength * Math.cos(theta);
+            particle.sy = petalLength * Math.sin(theta);
+            particle.ax = particle.sx * -0.5;
+            particle.ay = particle.sy * -0.5;
+        }
+    }
+    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
+
+            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/360.0, pos.y/600.0);
+            }
+        "
+        fragmentShader: "
+            varying highp vec2 fPos;
+            varying lowp float fFade;
+            varying highp vec2 fTex;
+            void main() {//*2 because this generates dark colors mostly
+                highp vec2 circlePos = fTex*2.0 - vec2(1.0,1.0);
+                highp float dist = length(circlePos);
+                highp float circleFactor = max(min(1.0 - dist, 1.0), 0.0);
+                gl_FragColor = vec4(fPos.x*2.0 - fPos.y, fPos.y*2.0 - fPos.x, fPos.x*fPos.y*2.0, 0.0) * circleFactor * fFade;
+            }"
+
+    }
+}
diff --git a/examples/declarative/particles/custom/fallingleaves.qml b/examples/declarative/particles/custom/fallingleaves.qml
new file mode 100644 (file)
index 0000000..e41d214
--- /dev/null
@@ -0,0 +1,97 @@
+import QtQuick 2.0
+import QtQuick.Particles 2.0
+
+Item {
+    width: 360
+    height: 600
+
+    Image{
+        source: "content/backgroundLeaves.jpg"
+        anchors.fill: parent
+    }
+    ParticleSystem{ id: sys }
+    Emitter{
+        system: sys
+        width: parent.width
+        emitRate: 6
+        lifeSpan: 12000
+        size: 80
+        speed: PointDirection{ y: 60 }
+    }
+    Wander {
+        system: sys
+        anchors.fill: parent
+        xVariance: 60
+        pace: 60
+    }
+    CustomAffector{
+        system: sys
+        property real coefficient: 0.5
+        property real speed: 10.0
+        onAffectParticle:{
+        /*  //Linear movement
+            if (particle.r == 0){
+                particle.r = Math.random() > 0.5 ? -1 : 1;
+            }else if (particle.r == 1){
+                particle.rotation += speed * dt;
+                if(particle.rotation >= maxAngle)
+                    particle.r = -1;
+            }else if (particle.r == -1){
+                particle.rotation -= speed * dt;
+                if(particle.rotation <= -1 * maxAngle)
+                    particle.r = 1;
+            }
+        */
+            //Wobbly movement
+            if (particle.r == 0.0){
+                particle.r = Math.random() + 0.01;
+            }
+            particle.rotation += speed * particle.r * dt;
+            particle.r -= particle.rotation * coefficient;
+            if (particle.r == 0.0)
+                particle.r -= particle.rotation * 0.000001;
+        }
+    }
+
+    ImageParticle{
+        anchors.fill: parent
+        id: particles
+        system: sys
+        sprites: [Sprite{
+                source: "content/realLeaf1.png"
+                frames: 1
+                duration: 1
+                to: {"a":1, "b":1, "c":1, "d":1}
+            }, Sprite{
+                name: "a"
+                source: "content/realLeaf1.png"
+                frames: 1
+                duration: 10000
+            },
+            Sprite{
+                name: "b"
+                source: "content/realLeaf2.png"
+                frames: 1
+                duration: 10000
+            },
+            Sprite{
+                name: "c"
+                source: "content/realLeaf3.png"
+                frames: 1
+                duration: 10000
+            },
+            Sprite{
+                name: "d"
+                source: "content/realLeaf4.png"
+                frames: 1
+                duration: 10000
+            }
+        ]
+
+        width: 100
+        height: 100
+        x: 20
+        y: 20
+        z:4
+    }
+}
index 5d2f49e..7bdd3af 100644 (file)
@@ -60,6 +60,8 @@ Rectangle{
             "../asteroid/asteroid.qml",
             "../asteroid/blackhole.qml",
             "../custom/blurparticles.qml",
+            "../custom/custom.qml",
+            "../custom/fallingleaves.qml",
             "../modelparticles/bubbles.qml",
             "../modelparticles/gridsplosion.qml",
             "../modelparticles/package.qml",
diff --git a/examples/declarative/particles/launcherContent/icons/custom.png b/examples/declarative/particles/launcherContent/icons/custom.png
new file mode 100644 (file)
index 0000000..6d49257
Binary files /dev/null and b/examples/declarative/particles/launcherContent/icons/custom.png differ
diff --git a/examples/declarative/particles/launcherContent/icons/fallingleaves.png b/examples/declarative/particles/launcherContent/icons/fallingleaves.png
new file mode 100644 (file)
index 0000000..d02e791
Binary files /dev/null and b/examples/declarative/particles/launcherContent/icons/fallingleaves.png differ
index bdd42ae..582e1d3 100644 (file)
@@ -28,7 +28,10 @@ HEADERS += \
     $$PWD/qsgturbulence_p.h \
     $$PWD/qsgwander_p.h \
     $$PWD/qsgtargetaffector_p.h \
-    $$PWD/qsgcumulativedirection_p.h
+    $$PWD/qsgcumulativedirection_p.h \
+    $$PWD/qsgcustomemitter_p.h \
+    $$PWD/qsgcustomaffector_p.h \
+    $$PWD/qsgv8particledata_p.h
 
 SOURCES += \
     $$PWD/qsgangleddirection.cpp \
@@ -58,7 +61,10 @@ SOURCES += \
     $$PWD/qsgturbulence.cpp \
     $$PWD/qsgwander.cpp \
     $$PWD/qsgtargetaffector.cpp \
-    $$PWD/qsgcumulativedirection.cpp
+    $$PWD/qsgcumulativedirection.cpp \
+    $$PWD/qsgcustomemitter.cpp \
+    $$PWD/qsgcustomaffector.cpp \
+    $$PWD/qsgv8particledata.cpp
 
 RESOURCES += \
     $$PWD/particles.qrc
diff --git a/src/declarative/particles/qsgcustomaffector.cpp b/src/declarative/particles/qsgcustomaffector.cpp
new file mode 100644 (file)
index 0000000..9832399
--- /dev/null
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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 "qsgcustomaffector_p.h"
+QT_BEGIN_NAMESPACE
+/*!
+    \qmlclass CustomAffector QSGCustomAffector
+    \inqmlmodule QtQuick.Particles 2
+    \since QtQuick.Particles 2.0
+    \inherits Affector
+    \brief The Custom affector allows you to modify affected particles
+
+*/
+
+//TODO: Document particle 'type'
+/*!
+    \qmlsignal Timer::affecting(particle, dt)
+
+    This handler is called when a particle is selected to be affected.
+
+    dt is the time since the last time it was affected. Use dt to normalize
+    trajectory manipulations to real time.
+*/
+QSGCustomAffector::QSGCustomAffector(QSGItem *parent) :
+    QSGParticleAffector(parent)
+{
+}
+
+
+bool QSGCustomAffector::affectParticle(QSGParticleData *d, qreal dt)
+{
+    emit affectParticle(d->v8Value(), dt);
+    return true;//TODO: Work it out (best added alongside autoTimeScaling)
+}
+QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgcustomaffector_p.h b/src/declarative/particles/qsgcustomaffector_p.h
new file mode 100644 (file)
index 0000000..b4d0ca7
--- /dev/null
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 CUSTOMAFFECTOR_H
+#define CUSTOMAFFECTOR_H
+#include "qsgparticleaffector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+
+class QSGCustomAffector : public QSGParticleAffector
+{
+    Q_OBJECT
+public:
+    explicit QSGCustomAffector(QSGItem *parent = 0);
+protected:
+    virtual bool affectParticle(QSGParticleData *d, qreal dt);
+signals:
+    void affectParticle(QDeclarativeV8Handle particle, qreal dt);
+
+public slots:
+
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // CUSTOMAFFECTOR_H
diff --git a/src/declarative/particles/qsgcustomemitter.cpp b/src/declarative/particles/qsgcustomemitter.cpp
new file mode 100644 (file)
index 0000000..4a379a3
--- /dev/null
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** 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 "qsgcustomemitter_p.h"
+#include "qsgparticlesystem_p.h"
+QT_BEGIN_NAMESPACE
+/*!
+    \qmlclass CustomEmitter QSGCustomEmitter
+    \inqmlmodule QtQuick.Particles 2
+    \since QtQuick.Particles 2.0
+    \inherits Emitter
+    \brief The Custom emitter allows you to modify particles as they are emitted
+
+*/
+
+//TODO: Document particle 'type'
+/*!
+    \qmlsignal CustomEmitter::emitting(particle)
+
+    This handler is called when a particle is emitted. You can modify particle
+    attributes from within the handler.
+*/
+
+QSGCustomEmitter::QSGCustomEmitter(QSGItem* parent)
+    : QSGParticleEmitter(parent)
+    , m_particle_count(0)
+    , m_reset_last(true)
+    , m_last_timestamp(0)
+    , m_last_emission(0)
+{
+}
+
+void QSGCustomEmitter::reset()
+{
+    m_reset_last = true;
+}
+
+void QSGCustomEmitter::emitWindow(int timeStamp)
+{
+    if (m_system == 0)
+        return;
+    if ((!m_emitting || !m_particlesPerSecond)&& !m_burstLeft && m_burstQueue.isEmpty()){
+        m_reset_last = true;
+        return;
+    }
+
+    if (m_reset_last) {
+        m_last_emitter = m_last_last_emitter = QPointF(x(), y());
+        m_last_timestamp = timeStamp/1000.;
+        m_last_emission = m_last_timestamp;
+        m_reset_last = false;
+    }
+
+    if (m_burstLeft){
+        m_burstLeft -= timeStamp - m_last_timestamp * 1000.;
+        if (m_burstLeft < 0){
+            if (!m_emitting)
+                timeStamp += m_burstLeft;
+            m_burstLeft = 0;
+        }
+    }
+
+    qreal time = timeStamp / 1000.;
+
+    qreal particleRatio = 1. / m_particlesPerSecond;
+    qreal pt = m_last_emission;
+
+    qreal opt = pt; // original particle time
+    qreal dt = time - m_last_timestamp; // timestamp delta...
+    if (!dt)
+        dt = 0.000001;
+
+    // emitter difference since last...
+    qreal dex = (x() - m_last_emitter.x());
+    qreal dey = (y() - m_last_emitter.y());
+
+    qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize;
+    qreal emitter_x_offset = m_last_emitter.x() - x();
+    qreal emitter_y_offset = m_last_emitter.y() - y();
+    if (!m_burstQueue.isEmpty() && !m_burstLeft && !m_emitting)//'outside time' emissions only
+        pt = time;
+    while (pt < time || !m_burstQueue.isEmpty()) {
+        //int pos = m_last_particle % m_particle_count;
+        QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle]);
+        if (datum){//actually emit(otherwise we've been asked to skip this one)
+            datum->e = this;//###useful?
+            qreal t = 1 - (pt - opt) / dt;
+
+
+            // Particle timestamp
+            datum->t = pt;
+            datum->lifeSpan = //TODO:Promote to base class?
+                    (m_particleDuration
+                     + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
+                    / 1000.0;
+
+            // Particle position
+            QRectF boundsRect;
+            if (!m_burstQueue.isEmpty()){
+                boundsRect = QRectF(m_burstQueue.first().second.x() - x(), m_burstQueue.first().second.y() - y(),
+                        width(), height());
+            } else {
+                boundsRect = QRectF(emitter_x_offset + dex * (pt - opt) / dt, emitter_y_offset + dey * (pt - opt) / dt
+                              , width(), height());
+            }
+            QPointF newPos = effectiveExtruder()->extrude(boundsRect);
+            datum->x = newPos.x();
+            datum->y = newPos.y();
+
+            // Particle speed
+            const QPointF &speed = m_speed->sample(newPos);
+            datum->sx = speed.x();
+            datum->sy = speed.y();
+
+            // Particle acceleration
+            const QPointF &accel = m_acceleration->sample(newPos);
+            datum->ax = accel.x();
+            datum->ay = accel.y();
+
+            // Particle size
+            float sizeVariation = -m_particleSizeVariation
+                    + rand() / float(RAND_MAX) * m_particleSizeVariation * 2;
+
+            float size = qMax((qreal)0.0 , m_particleSize + sizeVariation);
+            float endSize = qMax((qreal)0.0 , sizeAtEnd + sizeVariation);
+
+            datum->size = size;
+            datum->endSize = endSize;
+
+            emitParticle(datum->v8Value());//A chance for arbitrary JS changes
+
+            m_system->emitParticle(datum);
+        }
+        if (m_burstQueue.isEmpty()){
+            pt += particleRatio;
+        }else{
+            m_burstQueue.first().first--;
+            if (m_burstQueue.first().first <= 0)
+                m_burstQueue.pop_front();
+        }
+    }
+    m_last_emission = pt;
+
+    m_last_last_last_emitter = m_last_last_emitter;
+    m_last_last_emitter = m_last_emitter;
+    m_last_emitter = QPointF(x(), y());
+    m_last_timestamp = time;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgcustomemitter_p.h b/src/declarative/particles/qsgcustomemitter_p.h
new file mode 100644 (file)
index 0000000..4d22490
--- /dev/null
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** 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 QSGCUSTOMEMITTER_H
+#define QSGCUSTOMEMITTER_H
+#include "qsgparticleemitter_p.h"
+#include <private/qv8engine_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGCustomEmitter : public QSGParticleEmitter
+{
+    Q_OBJECT
+public:
+    explicit QSGCustomEmitter(QSGItem* parent=0);
+    virtual ~QSGCustomEmitter(){}
+    virtual void emitWindow(int timeStamp);
+    virtual void reset();
+Q_SIGNALS:
+    void emitParticle(QDeclarativeV8Handle particle);
+private:
+    int m_particle_count;
+    bool m_reset_last;
+    qreal m_last_timestamp;
+    qreal m_last_emission;
+
+    QPointF m_last_emitter;
+    QPointF m_last_last_emitter;
+    QPointF m_last_last_last_emitter;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // QSGCUSTOMEMITTER_H
index 0303c29..f61da15 100644 (file)
@@ -42,7 +42,7 @@
 #ifndef TRAILSEMITTER_H
 #define TRAILSEMITTER_H
 
-#include <QtCore>
+#include <QtCore>//FIXME
 #include <QtGui>
 
 #include "qsgparticleemitter_p.h"
index 330ff81..1bb06ea 100644 (file)
@@ -67,6 +67,8 @@
 #include "qsgwander_p.h"
 #include "qsgtargetaffector_p.h"
 #include "qsgcumulativedirection_p.h"
+#include "qsgcustomemitter_p.h"
+#include "qsgcustomaffector_p.h"
 
 QT_BEGIN_NAMESPACE
 
@@ -87,7 +89,8 @@ void QSGParticlesModule::defineModule()
     qmlRegisterType<QSGItemParticle>(uri, 2, 0, "ItemParticle");
     qmlRegisterType<QSGModelParticle>(uri, 2, 0, "ModelParticle");
 
-    qmlRegisterType<QSGBasicEmitter>(uri, 2, 0, "Emitter");
+    qmlRegisterType<QSGBasicEmitter>(uri, 2, 0, "Emitter");//TODO: Rename BasicEmitter?
+    qmlRegisterType<QSGCustomEmitter>(uri, 2, 0, "CustomEmitter");
     qmlRegisterType<QSGFollowEmitter>(uri, 2, 0, "FollowEmitter");
 
     qmlRegisterType<QSGEllipseExtruder>(uri, 2, 0, "EllipseShape");
@@ -99,7 +102,8 @@ void QSGParticlesModule::defineModule()
     qmlRegisterType<QSGTargetedDirection>(uri, 2, 0, "TargetedDirection");
     qmlRegisterType<QSGCumulativeDirection>(uri, 2, 0, "CumulativeDirection");
 
-    qmlRegisterType<QSGParticleAffector>(uri, 2, 0, "Affector");//for the triggered signal
+    qmlRegisterType<QSGParticleAffector>(uri, 2, 0, "Affector");//useful for the triggered signal
+    qmlRegisterType<QSGCustomAffector>(uri, 2, 0, "CustomAffector");
     qmlRegisterType<QSGWanderAffector>(uri, 2, 0, "Wander");
     qmlRegisterType<QSGFrictionAffector>(uri, 2, 0, "Friction");
     qmlRegisterType<QSGPointAttractorAffector>(uri, 2, 0, "PointAttractor");
index f25172d..6f4a617 100644 (file)
 #include "qsgparticlepainter_p.h"
 #include "qsgspriteengine_p.h"
 #include "qsgsprite_p.h"
+#include "qsgv8particledata_p.h"
 
 #include "qsgfollowemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
+#include <private/qdeclarativeengine_p.h>
 #include <cmath>
 #include <QDebug>
 
@@ -114,7 +116,7 @@ void QSGParticleDataHeap::grow() //###Consider automatic growth vs resize() call
     m_data.resize(1 << ++m_size);
 }
 
-void QSGParticleDataHeap::insert(QSGParticleData* data)
+void QSGParticleDataHeap::insert(QSGParticleData* data)//TODO: Optimize 0 lifespan (or already dead) case
 {
     int time = roundedTime(data->t + data->lifeSpan);
     if (m_lookups.contains(time)){
@@ -296,6 +298,7 @@ QSGParticleData::QSGParticleData(QSGParticleSystem* sys)
     , system(sys)
     , index(0)
     , systemIndex(-1)
+    , v8Datum(0)
 {
     x = 0;
     y = 0;
@@ -359,6 +362,12 @@ void QSGParticleData::clone(const QSGParticleData& other)
     modelIndex = other.modelIndex;
 }
 
+QDeclarativeV8Handle QSGParticleData::v8Value()
+{
+    if (!v8Datum)
+        v8Datum = new QSGV8ParticleData(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(system)), this);
+    return v8Datum->v8Value();
+}
 //sets the x accleration without affecting the instantaneous x velocity or position
 void QSGParticleData::setInstantaneousAX(qreal ax)
 {
index b9729f9..fd46ee2 100644 (file)
@@ -51,6 +51,7 @@
 #include <QtDeclarative/private/qsgsprite_p.h>
 #include <QAbstractAnimation>
 #include <QtDeclarative/qdeclarative.h>
+#include <private/qv8engine_p.h> //For QDeclarativeV8Handle
 
 QT_BEGIN_HEADER
 
@@ -66,6 +67,7 @@ class QSGParticleData;
 class QSGParticleSystemAnimation;
 class QSGSpriteEngine;
 class QSGSprite;
+class QSGV8ParticleData;
 
 struct QSGParticleDataHeapNode{
     int time;//in ms
@@ -204,6 +206,9 @@ public:
     float lifeLeft();
     float curSize();
     void clone(const QSGParticleData& other);//Not =, leaves meta-data like index
+    QDeclarativeV8Handle v8Value();
+private:
+    QSGV8ParticleData* v8Datum;
 };
 
 class QSGParticleSystem : public QSGItem
diff --git a/src/declarative/particles/qsgv8particledata.cpp b/src/declarative/particles/qsgv8particledata.cpp
new file mode 100644 (file)
index 0000000..6b31e37
--- /dev/null
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** 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 "qsgv8particledata_p.h"
+#include "qsgparticlesystem_p.h"//for QSGParticleData
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+//### Particle data handles are not locked to within certain scopes like QSGContext2D, but there's no way to reload either...
+class QV8ParticleDataResource : public QV8ObjectResource
+{
+    V8_RESOURCE_TYPE(ParticleDataType)
+public:
+    QV8ParticleDataResource(QV8Engine *e) : QV8ObjectResource(e) {}
+    QSGParticleData* datum;//TODO: Guard needed?
+};
+
+class QV8ParticleDataDeletable : public QV8Engine::Deletable
+{
+public:
+    QV8ParticleDataDeletable(QV8Engine *engine);
+    ~QV8ParticleDataDeletable();
+
+    v8::Persistent<v8::Function> constructor;
+};
+
+static v8::Handle<v8::Value> particleData_discard(const v8::Arguments &args)
+{
+    QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+
+    if (!r || !r->datum)
+        V8THROW_ERROR("Not a valid ParticleData object");
+
+    r->datum->lifeSpan = 0; //Don't kill(), because it could still be in the middle of being created
+    return v8::Undefined();
+}
+
+static v8::Handle<v8::Value> particleData_lifeLeft(const v8::Arguments &args)
+{
+    QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+    if (!r || !r->datum)
+        V8THROW_ERROR("Not a valid ParticleData object");
+
+    return v8::Number::New(r->datum->lifeLeft());
+}
+
+static v8::Handle<v8::Value> particleData_curSize(const v8::Arguments &args)
+{
+    QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(args.This());
+    if (!r || !r->datum)
+        V8THROW_ERROR("Not a valid ParticleData object");
+
+    return v8::Number::New(r->datum->curSize());
+}
+
+#define FLOAT_GETTER_AND_SETTER(VARIABLE) static v8::Handle<v8::Value> particleData_get_ ## VARIABLE (v8::Local<v8::String>, const v8::AccessorInfo &info) \
+{ \
+    QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This()); \
+    if (!r || !r->datum) \
+        V8THROW_ERROR("Not a valid ParticleData object"); \
+\
+    return v8::Number::New(r->datum-> VARIABLE);\
+}\
+\
+static void particleData_set_ ## VARIABLE (v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)\
+{\
+    QV8ParticleDataResource *r = v8_resource_cast<QV8ParticleDataResource>(info.This());\
+    if (!r || !r->datum)\
+        V8THROW_ERROR_SETTER("Not a valid ParticleData object");\
+\
+    r->datum-> VARIABLE = value->NumberValue();\
+}
+
+#define FLOAT_REGISTER_ACCESSOR(FT, ENGINE, VARIABLE) FT ->PrototypeTemplate()->SetAccessor( v8::String::New( #VARIABLE ), particleData_get_ ## VARIABLE , particleData_set_ ## VARIABLE , v8::External::Wrap(ENGINE))
+
+FLOAT_GETTER_AND_SETTER(x)
+FLOAT_GETTER_AND_SETTER(y)
+FLOAT_GETTER_AND_SETTER(t)
+FLOAT_GETTER_AND_SETTER(lifeSpan)
+FLOAT_GETTER_AND_SETTER(size)
+FLOAT_GETTER_AND_SETTER(endSize)
+FLOAT_GETTER_AND_SETTER(sx)
+FLOAT_GETTER_AND_SETTER(sy)
+FLOAT_GETTER_AND_SETTER(ax)
+FLOAT_GETTER_AND_SETTER(ay)
+FLOAT_GETTER_AND_SETTER(xx)
+FLOAT_GETTER_AND_SETTER(xy)
+FLOAT_GETTER_AND_SETTER(rotation)
+FLOAT_GETTER_AND_SETTER(rotationSpeed)
+FLOAT_GETTER_AND_SETTER(autoRotate)
+FLOAT_GETTER_AND_SETTER(animIdx)
+FLOAT_GETTER_AND_SETTER(frameDuration)
+FLOAT_GETTER_AND_SETTER(frameCount)
+FLOAT_GETTER_AND_SETTER(animT)
+FLOAT_GETTER_AND_SETTER(r)
+
+//TODO: Non-floats (color) and special floats (curX) once basic floats are working well
+
+QV8ParticleDataDeletable::QV8ParticleDataDeletable(QV8Engine *engine)
+{
+    v8::HandleScope handle_scope;
+    v8::Context::Scope scope(engine->context());
+
+    v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+    ft->InstanceTemplate()->SetHasExternalResource(true);
+    ft->PrototypeTemplate()->Set(v8::String::New("discard"), V8FUNCTION(particleData_discard, engine));
+    ft->PrototypeTemplate()->Set(v8::String::New("lifeLeft"), V8FUNCTION(particleData_lifeLeft, engine));
+    ft->PrototypeTemplate()->Set(v8::String::New("curSize"), V8FUNCTION(particleData_curSize, engine));
+    FLOAT_REGISTER_ACCESSOR(ft, engine, x);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, y);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, t);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, lifeSpan);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, size);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, endSize);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, sx);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, sy);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, ax);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, ay);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, xx);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, xy);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, rotation);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, rotationSpeed);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, autoRotate);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, animIdx);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, frameDuration);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, frameCount);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, animT);
+    FLOAT_REGISTER_ACCESSOR(ft, engine, r);
+
+    constructor = qPersistentNew(ft->GetFunction());
+}
+
+QV8ParticleDataDeletable::~QV8ParticleDataDeletable()
+{
+    qPersistentDispose(constructor);
+}
+
+V8_DEFINE_EXTENSION(QV8ParticleDataDeletable, particleV8Data);
+
+
+QSGV8ParticleData::QSGV8ParticleData(QV8Engine* engine, QSGParticleData* datum)
+{
+    if (!engine || !datum)
+        return;
+    v8::HandleScope handle_scope;
+    v8::Context::Scope scope(engine->context());
+
+    QV8ParticleDataDeletable *d = particleV8Data(engine);
+    m_v8Value = qPersistentNew(d->constructor->NewInstance());
+    QV8ParticleDataResource *r = new QV8ParticleDataResource(engine);
+    r->datum = datum;
+    m_v8Value->SetExternalResource(r);
+}
+
+QSGV8ParticleData::~QSGV8ParticleData()
+{
+    qPersistentDispose(m_v8Value);
+}
+
+QDeclarativeV8Handle QSGV8ParticleData::v8Value()
+{
+    return QDeclarativeV8Handle::fromHandle(m_v8Value);
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/particles/qsgv8particledata_p.h b/src/declarative/particles/qsgv8particledata_p.h
new file mode 100644 (file)
index 0000000..e4a78a9
--- /dev/null
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 QSGV8PARTICLEDATA_H
+#define QSGV8PARTICLEDATA_H
+
+#include <private/qv8engine_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGParticleData;
+class QSGV8ParticleData {
+public:
+    QSGV8ParticleData(QV8Engine*,QSGParticleData*);
+    ~QSGV8ParticleData();
+    QDeclarativeV8Handle v8Value();
+private:
+    v8::Persistent<v8::Object> m_v8Value;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif
index 2dd52b0..0569242 100644 (file)
@@ -188,6 +188,7 @@ QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
             case QV8ObjectResource::SQLDatabaseType:
             case QV8ObjectResource::ListModelType:
             case QV8ObjectResource::Context2DType:
+            case QV8ObjectResource::ParticleDataType:
                 return QVariant();
             case QV8ObjectResource::QObjectType:
                 return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r));
index d9f4fee..5058eee 100644 (file)
@@ -125,7 +125,7 @@ public:
     QV8ObjectResource(QV8Engine *engine) : engine(engine) { Q_ASSERT(engine); }
     enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType, 
                         ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType,
-                        ListModelType, Context2DType };
+                        ListModelType, Context2DType, ParticleDataType };
     virtual ResourceType resourceType() const = 0;
 
     QV8Engine *engine;