diff options
Diffstat (limited to 'src/particles')
65 files changed, 12672 insertions, 0 deletions
diff --git a/src/particles/particleresources/noise.png b/src/particles/particleresources/noise.png Binary files differnew file mode 100644 index 0000000000..3c723e1a5a --- /dev/null +++ b/src/particles/particleresources/noise.png diff --git a/src/particles/particles.pri b/src/particles/particles.pri new file mode 100644 index 0000000000..3e083ab291 --- /dev/null +++ b/src/particles/particles.pri @@ -0,0 +1,70 @@ +HEADERS += \ + $$PWD/qquickangledirection_p.h \ + $$PWD/qquickcustomparticle_p.h \ + $$PWD/qquickcustomaffector_p.h \ + $$PWD/qquickellipseextruder_p.h \ + $$PWD/qquicktrailemitter_p.h \ + $$PWD/qquickfriction_p.h \ + $$PWD/qquickgravity_p.h \ + $$PWD/qquickimageparticle_p.h \ + $$PWD/qquickitemparticle_p.h \ + $$PWD/qquickage_p.h \ + $$PWD/qquicklineextruder_p.h \ + $$PWD/qquickmaskextruder_p.h \ + $$PWD/qquickparticleaffector_p.h \ + $$PWD/qquickparticleemitter_p.h \ + $$PWD/qquickparticleextruder_p.h \ + $$PWD/qquickparticlepainter_p.h \ + $$PWD/qquickparticlesmodule_p.h \ + $$PWD/qquickparticlesystem_p.h \ + $$PWD/qquickpointattractor_p.h \ + $$PWD/qquickpointdirection_p.h \ + $$PWD/qquickspritegoal_p.h \ + $$PWD/qquickdirection_p.h \ + $$PWD/qquicktargetdirection_p.h \ + $$PWD/qquickturbulence_p.h \ + $$PWD/qquickwander_p.h \ + $$PWD/qquickcumulativedirection_p.h \ + $$PWD/qquickv8particledata_p.h \ + $$PWD/qquickrectangleextruder_p.h \ + $$PWD/qquickparticlegroup_p.h \ + $$PWD/qquickgroupgoal_p.h \ + $$PWD/qtquickparticlesglobal_p.h + +SOURCES += \ + $$PWD/qquickangledirection.cpp \ + $$PWD/qquickcustomparticle.cpp \ + $$PWD/qquickcustomaffector.cpp \ + $$PWD/qquickellipseextruder.cpp \ + $$PWD/qquicktrailemitter.cpp \ + $$PWD/qquickfriction.cpp \ + $$PWD/qquickgravity.cpp \ + $$PWD/qquickimageparticle.cpp \ + $$PWD/qquickitemparticle.cpp \ + $$PWD/qquickage.cpp \ + $$PWD/qquicklineextruder.cpp \ + $$PWD/qquickmaskextruder.cpp \ + $$PWD/qquickparticleaffector.cpp \ + $$PWD/qquickparticleemitter.cpp \ + $$PWD/qquickparticleextruder.cpp \ + $$PWD/qquickparticlepainter.cpp \ + $$PWD/qquickparticlesmodule.cpp \ + $$PWD/qquickparticlesystem.cpp \ + $$PWD/qquickpointattractor.cpp \ + $$PWD/qquickpointdirection.cpp \ + $$PWD/qquickspritegoal.cpp \ + $$PWD/qquickdirection.cpp \ + $$PWD/qquicktargetdirection.cpp \ + $$PWD/qquickturbulence.cpp \ + $$PWD/qquickwander.cpp \ + $$PWD/qquickcumulativedirection.cpp \ + $$PWD/qquickv8particledata.cpp \ + $$PWD/qquickrectangleextruder.cpp \ + $$PWD/qquickparticlegroup.cpp \ + $$PWD/qquickgroupgoal.cpp + +RESOURCES += \ + $$PWD/particles.qrc + + + diff --git a/src/particles/particles.pro b/src/particles/particles.pro new file mode 100644 index 0000000000..3aff6bbcc0 --- /dev/null +++ b/src/particles/particles.pro @@ -0,0 +1,40 @@ +load(qt_module) + +TARGET = QtQuickParticles + +CONFIG += module +CONFIG += dll warn_on +MODULE_PRI = ../../modules/qt_quickparticles.pri + +QT = core-private gui-private v8-private qml-private quick-private + +DEFINES += QT_BUILD_QUICKPARTICLES_LIB QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES +win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS +solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 + +exists("qqml_enable_gcov") { + QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors + LIBS += -lgcov +} + +MODULE = quickparticles +load(qt_module_config) + +include(particles.pri) + +mac { + # FIXME: this is a workaround for broken qmake logic in qtAddModule() + # This function refuses to use frameworks unless the framework exists on + # the filesystem at the time qmake is run, resulting in a build failure + # if QtQuick is qmaked before QtQml is built and frameworks are + # in use. qtAddLibrary() contains correct logic to deal with this, so + # we'll explicitly call that for now. + load(qt) + LIBS -= -lQtQml # in non-framework builds, these should be re-added + LIBS -= -lQtQml_debug # within the qtAddLibrary if appropriate, so no + qtAddLibrary(QtQml) # harm done :) + LIBS -= -lQtQuick + LIBS -= -lQtQuick_debug + qtAddLibrary(QtQuick) +} + diff --git a/src/particles/particles.qrc b/src/particles/particles.qrc new file mode 100644 index 0000000000..344f9489a4 --- /dev/null +++ b/src/particles/particles.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>particleresources/noise.png</file> + </qresource> +</RCC> diff --git a/src/particles/qquickage.cpp b/src/particles/qquickage.cpp new file mode 100644 index 0000000000..9f24cba3cc --- /dev/null +++ b/src/particles/qquickage.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickage_p.h" +#include "qquickparticleemitter_p.h" +QT_BEGIN_NAMESPACE +/*! + \qmlclass Age QQuickAgeAffector + \inqmlmodule QtQuick.Particles 2 + \inherits Affector + \brief The Age affector allows you to prematurely age particles + + The Age affector allows you to alter where the particle is in its lifecycle. Common uses + are to expire particles prematurely, possibly giving them time to animate out. + + The Age affector only applies to particles which are still alive. +*/ +/*! + \qmlproperty int QtQuick.Particles2::Age::lifeLeft + + The amount of life to set the particle to have. Affected particles + will advance to a point in their life where they will have this many + milliseconds left to live. +*/ + +/*! + \qmlproperty bool QtQuick.Particles2::Age::advancePosition + + advancePosition determines whether position, veclocity and acceleration are included in + the simulated aging done by the affector. If advancePosition is false, + then the position, velocity and acceleration will remain the same and only + other attributes (such as opacity) will advance in the simulation to where + it would normally be for that point in the particle's life. With advancePosition set to + true the position, velocity and acceleration will also advance to where it would + normally be by that point in the particle's life, making it advance its position + on screen. + + Default value is true. +*/ + +QQuickAgeAffector::QQuickAgeAffector(QQuickItem *parent) : + QQuickParticleAffector(parent), m_lifeLeft(0), m_advancePosition(true) +{ +} + + +bool QQuickAgeAffector::affectParticle(QQuickParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + if (d->stillAlive()){ + qreal curT = (qreal)m_system->timeInt/1000.0; + qreal ttl = (qreal)m_lifeLeft/1000.0; + if (!m_advancePosition && ttl > 0){ + qreal x = d->curX(); + qreal vx = d->curVX(); + qreal ax = d->curAX(); + qreal y = d->curY(); + qreal vy = d->curVY(); + qreal ay = d->curAY(); + d->t = curT - (d->lifeSpan - ttl); + d->setInstantaneousX(x); + d->setInstantaneousVX(vx); + d->setInstantaneousAX(ax); + d->setInstantaneousY(y); + d->setInstantaneousVY(vy); + d->setInstantaneousAY(ay); + } else { + d->t = curT - (d->lifeSpan - ttl); + } + return true; + } + return false; +} +QT_END_NAMESPACE diff --git a/src/particles/qquickage_p.h b/src/particles/qquickage_p.h new file mode 100644 index 0000000000..2ec8faae4b --- /dev/null +++ b/src/particles/qquickage_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 KILLAFFECTOR_H +#define KILLAFFECTOR_H +#include "qquickparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickAgeAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(int lifeLeft READ lifeLeft WRITE setLifeLeft NOTIFY lifeLeftChanged) + Q_PROPERTY(bool advancePosition READ advancePosition WRITE setAdvancePosition NOTIFY advancePositionChanged) + +public: + explicit QQuickAgeAffector(QQuickItem *parent = 0); + + int lifeLeft() const + { + return m_lifeLeft; + } + + bool advancePosition() const + { + return m_advancePosition; + } + +protected: + virtual bool affectParticle(QQuickParticleData *d, qreal dt); +signals: + void lifeLeftChanged(int arg); + void advancePositionChanged(bool arg); + +public slots: + void setLifeLeft(int arg) + { + if (m_lifeLeft != arg) { + m_lifeLeft = arg; + emit lifeLeftChanged(arg); + } + } + + void setAdvancePosition(bool arg) + { + if (m_advancePosition != arg) { + m_advancePosition = arg; + emit advancePositionChanged(arg); + } + } + +private: + int m_lifeLeft; + bool m_advancePosition; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // KILLAFFECTOR_H diff --git a/src/particles/qquickangledirection.cpp b/src/particles/qquickangledirection.cpp new file mode 100644 index 0000000000..e77c47362c --- /dev/null +++ b/src/particles/qquickangledirection.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickangledirection_p.h" +#include <stdlib.h> +#include <cmath> +#ifdef Q_OS_QNX +#include <math.h> +#endif +QT_BEGIN_NAMESPACE +const qreal CONV = 0.017453292519943295; +/*! + \qmlclass AngleDirection QQuickAngleDirection + \inqmlmodule QtQuick.Particles 2 + \inherits Direction + \brief The AngleDirection element allows you to specify a direction that varies in angle + + The AngledDirection element allows both the specification of a direction by angle and magnitude, + as well as varying the parameters by angle or magnitude. +*/ +/*! + \qmlproperty real QtQuick.Particles2::AngleDirection::angle + This property specifies the base angle for the direction. + The angle of this direction will vary by no more than angleVariation + from this angle. + + Angle is specified by degrees clockwise from straight right. + + The default value is zero. +*/ +/*! + \qmlproperty real QtQuick.Particles2::AngleDirection::magnitude + This property specifies the base magnitude for the direction. + The magnitude of this direction will vary by no more than magnitudeVariation + from this magnitude. + + Magnitude is specified in units of pixels per second. + + The default value is zero. +*/ +/*! + \qmlproperty real QtQuick.Particles2::AngleDirection::angleVariation + This property specifies the maximum angle variation for the direction. + The angle of the direction will vary by up to angleVariation clockwise + and anticlockwise from the value specified in angle. + + Angle is specified by degrees clockwise from straight right. + + The default value is zero. +*/ +/*! + \qmlproperty real QtQuick.Particles2::AngleDirection::magnitudeVariation + This property specifies the base magnitude for the direction. + The magnitude of this direction will vary by no more than magnitudeVariation + from the base magnitude. + + Magnitude is specified in units of pixels per second. + + The default value is zero. +*/ +QQuickAngleDirection::QQuickAngleDirection(QObject *parent) : + QQuickDirection(parent) + , m_angle(0) + , m_magnitude(0) + , m_angleVariation(0) + , m_magnitudeVariation(0) +{ + +} + +const QPointF QQuickAngleDirection::sample(const QPointF &from) +{ + Q_UNUSED(from); + QPointF ret; + qreal theta = m_angle*CONV - m_angleVariation*CONV + rand()/float(RAND_MAX) * m_angleVariation*CONV * 2; + qreal mag = m_magnitude- m_magnitudeVariation + rand()/float(RAND_MAX) * m_magnitudeVariation * 2; + ret.setX(mag * cos(theta)); + ret.setY(mag * sin(theta)); + return ret; +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickangledirection_p.h b/src/particles/qquickangledirection_p.h new file mode 100644 index 0000000000..84633cd40e --- /dev/null +++ b/src/particles/qquickangledirection_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 QQuickANGLEDDIRECTION_H +#define QQuickANGLEDDIRECTION_H +#include "qquickdirection_p.h" +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickAngleDirection : public QQuickDirection +{ + Q_OBJECT + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged) + Q_PROPERTY(qreal angleVariation READ angleVariation WRITE setAngleVariation NOTIFY angleVariationChanged) + Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged) +public: + explicit QQuickAngleDirection(QObject *parent = 0); + const QPointF sample(const QPointF &from); + qreal angle() const + { + return m_angle; + } + + qreal magnitude() const + { + return m_magnitude; + } + + qreal angleVariation() const + { + return m_angleVariation; + } + + qreal magnitudeVariation() const + { + return m_magnitudeVariation; + } + +signals: + + void angleChanged(qreal arg); + + void magnitudeChanged(qreal arg); + + void angleVariationChanged(qreal arg); + + void magnitudeVariationChanged(qreal arg); + +public slots: +void setAngle(qreal arg) +{ + if (m_angle != arg) { + m_angle = arg; + emit angleChanged(arg); + } +} + +void setMagnitude(qreal arg) +{ + if (m_magnitude != arg) { + m_magnitude = arg; + emit magnitudeChanged(arg); + } +} + +void setAngleVariation(qreal arg) +{ + if (m_angleVariation != arg) { + m_angleVariation = arg; + emit angleVariationChanged(arg); + } +} + +void setMagnitudeVariation(qreal arg) +{ + if (m_magnitudeVariation != arg) { + m_magnitudeVariation = arg; + emit magnitudeVariationChanged(arg); + } +} + +private: +qreal m_angle; +qreal m_magnitude; +qreal m_angleVariation; +qreal m_magnitudeVariation; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // QQuickANGLEDDIRECTION_H diff --git a/src/particles/qquickcumulativedirection.cpp b/src/particles/qquickcumulativedirection.cpp new file mode 100644 index 0000000000..d7c4094297 --- /dev/null +++ b/src/particles/qquickcumulativedirection.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickcumulativedirection_p.h" +QT_BEGIN_NAMESPACE + +/*! + \qmlclass CumulativeDirection QQuickCumulativeDirection + \inqmlmodule QtQuick.Particles 2 + \inherits Direction + \brief The CumulativeDirection element allows you to specify a direction made of other directions + + The CumulativeDirection element will act as a direction that sums the directions within it. +*/ +QQuickCumulativeDirection::QQuickCumulativeDirection(QObject *parent):QQuickDirection(parent) +{ +} + +QQmlListProperty<QQuickDirection> QQuickCumulativeDirection::directions() +{ + return QQmlListProperty<QQuickDirection>(this, m_directions);//TODO: Proper list property +} + +const QPointF QQuickCumulativeDirection::sample(const QPointF &from) +{ + QPointF ret; + foreach (QQuickDirection* dir, m_directions) + ret += dir->sample(from); + return ret; +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickcumulativedirection_p.h b/src/particles/qquickcumulativedirection_p.h new file mode 100644 index 0000000000..e1675a18d8 --- /dev/null +++ b/src/particles/qquickcumulativedirection_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 QQuickCUMULATIVEDIRECTION_P_H +#define QQuickCUMULATIVEDIRECTION_P_H +#include "qquickdirection_p.h" +#include <QQmlListProperty> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickCumulativeDirection : public QQuickDirection +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<QQuickDirection> directions READ directions) + Q_CLASSINFO("DefaultProperty", "directions") +public: + explicit QQuickCumulativeDirection(QObject *parent = 0); + QQmlListProperty<QQuickDirection> directions(); + const QPointF sample(const QPointF &from); +private: + QList<QQuickDirection*> m_directions; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQuickCUMULATIVEDIRECTION_P_H diff --git a/src/particles/qquickcustomaffector.cpp b/src/particles/qquickcustomaffector.cpp new file mode 100644 index 0000000000..acec98192d --- /dev/null +++ b/src/particles/qquickcustomaffector.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickcustomaffector_p.h" +#include <private/qv8engine_p.h> +#include <private/qqmlengine_p.h> +#include <QQmlEngine> +#include <QDebug> +QT_BEGIN_NAMESPACE + +//TODO: Move docs (and inheritence) to real base when docs can propagate. Currently this pretends to be the base class! +/*! + \qmlsignal QtQuick.Particles2::Affector::affectParticles(Array particles, real dt) + + This handler is called when particles are selected to be affected. particles contains + an array of particle objects which can be directly manipulated. + + dt is the time since the last time it was affected. Use dt to normalize + trajectory manipulations to real time. + + Note that JS is slower to execute, so it is not recommended to use this in + high-volume particle systems. +*/ + +/*! + \qmlproperty StochasticDirection QtQuick.Particles2::Affector::position + + Affected particles will have their position set to this direction, + relative to the ParticleSystem. When interpreting directions as points, + imagine it as an arrow with the base at the 0,0 of the ParticleSystem and the + tip at where the specified position will be. +*/ + +/*! + \qmlproperty StochasticDirection QtQuick.Particles2::Affector::speed + + Affected particles will have their speed set to this direction. +*/ + + +/*! + \qmlproperty StochasticDirection QtQuick.Particles2::Affector::acceleration + + Affected particles will have their acceleration set to this direction. +*/ + + +/*! + \qmlproperty bool QtQuick.Particles2::Affector::relative + + Whether the affected particles have their existing position/speed/acceleration added + to the new one. + + Default is true. +*/ +QQuickCustomAffector::QQuickCustomAffector(QQuickItem *parent) : + QQuickParticleAffector(parent) + , m_position(&m_nullVector) + , m_speed(&m_nullVector) + , m_acceleration(&m_nullVector) + , m_relative(true) +{ +} + +bool QQuickCustomAffector::isAffectConnected() +{ + static int idx = QObjectPrivate::get(this)->signalIndex("affectParticles(QQmlV8Handle,qreal)"); + return QObjectPrivate::get(this)->isSignalConnected(idx); +} + +void QQuickCustomAffector::affectSystem(qreal dt) +{ + if (!isAffectConnected()) { + QQuickParticleAffector::affectSystem(dt); + return; + } + if (!m_enabled) + return; + updateOffsets(); + + QList<QQuickParticleData*> toAffect; + foreach (QQuickParticleGroupData* gd, m_system->groupData) + if (activeGroup(m_system->groupData.key(gd))) + foreach (QQuickParticleData* d, gd->data) + if (shouldAffect(d)) + toAffect << d; + + if (toAffect.isEmpty()) + return; + + if (m_onceOff) + dt = 1.0; + + v8::HandleScope handle_scope; + v8::Context::Scope scope(QQmlEnginePrivate::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()); + + if (dt >= simulationCutoff || dt <= simulationDelta) { + affectProperties(toAffect, dt); + emit affectParticles(QQmlV8Handle::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; + affectProperties(toAffect, simulationDelta); + emit affectParticles(QQmlV8Handle::fromHandle(array), simulationDelta); + } + m_system->timeInt = realTime; + if (dt > 0.0) { + affectProperties(toAffect, dt); + emit affectParticles(QQmlV8Handle::fromHandle(array), dt); + } + } + + foreach (QQuickParticleData* d, toAffect) + if (d->update == 1.0) + postAffect(d); +} + +bool QQuickCustomAffector::affectParticle(QQuickParticleData *d, qreal dt) +{ + //This does the property based affecting, called by superclass if signal isn't hooked up. + bool changed = false; + QPointF curPos(d->curX(), d->curY()); + + if (m_acceleration != &m_nullVector){ + QPointF pos = m_acceleration->sample(curPos); + if (m_relative) { + pos *= dt; + pos += QPointF(d->curAX(), d->curAY()); + } + d->setInstantaneousAX(pos.x()); + d->setInstantaneousAY(pos.y()); + changed = true; + } + + if (m_speed != &m_nullVector){ + QPointF pos = m_speed->sample(curPos); + if (m_relative) { + pos *= dt; + pos += QPointF(d->curVX(), d->curVY()); + } + d->setInstantaneousVX(pos.x()); + d->setInstantaneousVY(pos.y()); + changed = true; + } + + if (m_position != &m_nullVector){ + QPointF pos = m_position->sample(curPos); + if (m_relative) { + pos *= dt; + pos += curPos; + } + d->setInstantaneousX(pos.x()); + d->setInstantaneousY(pos.y()); + changed = true; + } + + return changed; +} + +void QQuickCustomAffector::affectProperties(const QList<QQuickParticleData*> particles, qreal dt) +{ + foreach (QQuickParticleData* d, particles) + if ( affectParticle(d, dt) ) + d->update = 1.0; +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickcustomaffector_p.h b/src/particles/qquickcustomaffector_p.h new file mode 100644 index 0000000000..1266830f94 --- /dev/null +++ b/src/particles/qquickcustomaffector_p.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 <QObject> +#include "qquickparticlesystem_p.h" +#include "qquickparticleextruder_p.h" +#include "qquickparticleaffector_p.h" +#include "qquickdirection_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickCustomAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(bool relative READ relative WRITE setRelative NOTIFY relativeChanged) + Q_PROPERTY(QQuickDirection *position READ position WRITE setPosition NOTIFY positionChanged RESET positionReset) + Q_PROPERTY(QQuickDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged RESET speedReset) + Q_PROPERTY(QQuickDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged RESET accelerationReset) + +public: + explicit QQuickCustomAffector(QQuickItem *parent = 0); + virtual void affectSystem(qreal dt); + + QQuickDirection * position() const + { + return m_position; + } + + QQuickDirection * speed() const + { + return m_speed; + } + + QQuickDirection * acceleration() const + { + return m_acceleration; + } + + void positionReset() + { + m_position = &m_nullVector; + } + + void speedReset() + { + m_speed = &m_nullVector; + } + + void accelerationReset() + { + m_acceleration = &m_nullVector; + } + + bool relative() const + { + return m_relative; + } + + +signals: + void affectParticles(QQmlV8Handle particles, qreal dt); + + void positionChanged(QQuickDirection * arg); + + void speedChanged(QQuickDirection * arg); + + void accelerationChanged(QQuickDirection * arg); + + void relativeChanged(bool arg); + +public slots: + void setPosition(QQuickDirection * arg) + { + if (m_position != arg) { + m_position = arg; + emit positionChanged(arg); + } + } + + void setSpeed(QQuickDirection * arg) + { + if (m_speed != arg) { + m_speed = arg; + emit speedChanged(arg); + } + } + + void setAcceleration(QQuickDirection * arg) + { + if (m_acceleration != arg) { + m_acceleration = arg; + emit accelerationChanged(arg); + } + } + + void setRelative(bool arg) + { + if (m_relative != arg) { + m_relative = arg; + emit relativeChanged(arg); + } + } + +protected: + bool isAffectConnected(); + virtual bool affectParticle(QQuickParticleData *d, qreal dt); +private: + void affectProperties(const QList<QQuickParticleData*> particles, qreal dt); + QQuickDirection * m_position; + QQuickDirection * m_speed; + QQuickDirection * m_acceleration; + + QQuickDirection m_nullVector; + bool m_relative; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // CUSTOMAFFECTOR_H diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp new file mode 100644 index 0000000000..7d27e48c6b --- /dev/null +++ b/src/particles/qquickcustomparticle.cpp @@ -0,0 +1,473 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 "qquickcustomparticle_p.h" +#include <QtQuick/private/qquickshadereffectmesh_p.h> +#include <cstdlib> + +QT_BEGIN_NAMESPACE + +//Includes comments because the code isn't self explanatory +static const char qt_particles_template_vertex_code[] = + "attribute highp vec2 qt_ParticlePos;\n" + "attribute highp vec2 qt_ParticleTex;\n" + "attribute highp vec4 qt_ParticleData; // x = time, y = lifeSpan, z = size, w = endSize\n" + "attribute highp vec4 qt_ParticleVec; // x,y = constant speed, z,w = acceleration\n" + "attribute highp float qt_ParticleR;\n" + "uniform highp mat4 qt_Matrix;\n" + "uniform highp float qt_Timestamp;\n" + "varying highp vec2 qt_TexCoord0;\n" + "void defaultMain() {\n" + " qt_TexCoord0 = qt_ParticleTex;\n" + " highp float size = qt_ParticleData.z;\n" + " highp float endSize = qt_ParticleData.w;\n" + " highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;\n" + " highp float currentSize = mix(size, endSize, t * t);\n" + " if (t < 0. || t > 1.)\n" + " currentSize = 0.;\n" + " highp vec2 pos = qt_ParticlePos\n" + " - currentSize / 2. + currentSize * qt_ParticleTex // adjust size\n" + " + qt_ParticleVec.xy * t * qt_ParticleData.y // apply speed vector..\n" + " + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);\n" + " gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n" + "}"; +static const char qt_particles_default_vertex_code[] = + "void main() { \n" + " defaultMain(); \n" + "}"; + +static const char qt_particles_default_fragment_code[] = + "uniform sampler2D source; \n" + "varying highp vec2 qt_TexCoord0; \n" + "uniform lowp float qt_Opacity; \n" + "void main() { \n" + " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n" + "}"; + +static QSGGeometry::Attribute PlainParticle_Attributes[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position + QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // TexCoord + QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Data + QSGGeometry::Attribute::create(3, 4, GL_FLOAT), // Vectors + QSGGeometry::Attribute::create(4, 1, GL_FLOAT) // r +}; + +static QSGGeometry::AttributeSet PlainParticle_AttributeSet = +{ + 5, // Attribute Count + (2 + 2 + 4 + 4 + 1) * sizeof(float), + PlainParticle_Attributes +}; + +struct PlainVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float vx; + float vy; + float ax; + float ay; + float r; +}; + +struct PlainVertices { + PlainVertex v1; + PlainVertex v2; + PlainVertex v3; + PlainVertex v4; +}; + +/*! + \qmlclass CustomParticle QQuickCustomParticle + \inqmlmodule QtQuick.Particles 2 + \inherits ParticlePainter + \brief The CustomParticle element allows you to specify your own shader to paint particles. + +*/ + +QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent) + : QQuickParticlePainter(parent) + , m_dirtyUniforms(true) + , m_dirtyUniformValues(true) + , m_dirtyTextureProviders(true) + , m_dirtyProgram(true) +{ + setFlag(QQuickItem::ItemHasContents); +} + +void QQuickCustomParticle::sceneGraphInvalidated() +{ + m_nodes.clear(); +} + +QQuickCustomParticle::~QQuickCustomParticle() +{ +} + +void QQuickCustomParticle::componentComplete() +{ + m_common.updateShader(this, Key::FragmentShader); + updateVertexShader(); + reset(); + QQuickParticlePainter::componentComplete(); +} + + +//Trying to keep the shader conventions the same as in qsgshadereffectitem +/*! + \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader + + This property holds the fragment shader's GLSL source code. + The default shader expects the texture coordinate to be passed from the + vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a + sampler2D named "source". +*/ + +void QQuickCustomParticle::setFragmentShader(const QByteArray &code) +{ + if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData()) + return; + m_common.source.sourceCode[Key::FragmentShader] = code; + m_dirtyProgram = true; + if (isComponentComplete()) { + m_common.updateShader(this, Key::FragmentShader); + reset(); + } + emit fragmentShaderChanged(); +} + +/*! + \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader + + This property holds the vertex shader's GLSL source code. + + The default shader passes the texture coordinate along to the fragment + shader as "varying highp vec2 qt_TexCoord0". + + To aid writing a particle vertex shader, the following GLSL code is prepended + to your vertex shader: + \code + attribute highp vec2 qt_ParticlePos; + attribute highp vec2 qt_ParticleTex; + attribute highp vec4 qt_ParticleData; // x = time, y = lifeSpan, z = size, w = endSize + attribute highp vec4 qt_ParticleVec; // x,y = constant speed, z,w = acceleration + attribute highp float qt_ParticleR; + uniform highp mat4 qt_Matrix; + uniform highp float qt_Timestamp; + varying highp vec2 qt_TexCoord0; + void defaultMain() { + qt_TexCoord0 = qt_ParticleTex; + highp float size = qt_ParticleData.z; + highp float endSize = qt_ParticleData.w; + highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y; + highp float currentSize = mix(size, endSize, t * t); + if (t < 0. || t > 1.) + currentSize = 0.; + highp vec2 pos = qt_ParticlePos + - currentSize / 2. + currentSize * qt_ParticleTex // adjust size + + qt_ParticleVec.xy * t * qt_ParticleData.y // apply speed vector.. + + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.); + gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1); + } + \endcode + + defaultMain() is the same code as in the default shader, you can call this for basic + particle functions and then add additional variables for custom effects. Note that + the vertex shader for particles is responsible for simulating the movement of particles + over time, the particle data itself only has the starting position and spawn time. +*/ + +void QQuickCustomParticle::setVertexShader(const QByteArray &code) +{ + if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData()) + return; + m_common.source.sourceCode[Key::VertexShader] = code; + + m_dirtyProgram = true; + if (isComponentComplete()) { + updateVertexShader(); + reset(); + } + emit vertexShaderChanged(); +} + +void QQuickCustomParticle::updateVertexShader() +{ + m_common.disconnectPropertySignals(this, Key::VertexShader); + qDeleteAll(m_common.signalMappers[Key::VertexShader]); + m_common.uniformData[Key::VertexShader].clear(); + m_common.signalMappers[Key::VertexShader].clear(); + m_common.attributes.clear(); + m_common.attributes.append("qt_ParticlePos"); + m_common.attributes.append("qt_ParticleTex"); + m_common.attributes.append("qt_ParticleData"); + m_common.attributes.append("qt_ParticleVec"); + m_common.attributes.append("qt_ParticleR"); + + UniformData d; + d.name = "qt_Matrix"; + d.specialType = UniformData::Matrix; + m_common.uniformData[Key::VertexShader].append(d); + m_common.signalMappers[Key::VertexShader].append(0); + + d.name = "qt_Timestamp"; + d.specialType = UniformData::None; + m_common.uniformData[Key::VertexShader].append(d); + m_common.signalMappers[Key::VertexShader].append(0); + + const QByteArray &code = m_common.source.sourceCode[Key::VertexShader]; + if (!code.isEmpty()) + m_common.lookThroughShaderCode(this, Key::VertexShader, code); + + m_common.connectPropertySignals(this, Key::VertexShader); +} + +void QQuickCustomParticle::reset() +{ + QQuickParticlePainter::reset(); + update(); +} + +QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode); + if (m_pleaseReset){ + delete rootNode;//Automatically deletes children + rootNode = 0; + m_nodes.clear(); + m_pleaseReset = false; + m_dirtyProgram = true; + } + + if (m_system && m_system->isRunning() && !m_system->isPaused()){ + rootNode = prepareNextFrame(rootNode); + if (rootNode) + update(); + } + + return rootNode; +} + +QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode) +{ + if (!rootNode) + rootNode = buildCustomNodes(); + + if (!rootNode) + return 0; + + if (m_dirtyProgram) { + QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material()); + Q_ASSERT(material); + + Key s = m_common.source; + if (s.sourceCode[Key::FragmentShader].isEmpty()) + s.sourceCode[Key::FragmentShader] = qt_particles_default_fragment_code; + if (s.sourceCode[Key::VertexShader].isEmpty()) + s.sourceCode[Key::VertexShader] = qt_particles_default_vertex_code; + s.sourceCode[Key::VertexShader] = qt_particles_template_vertex_code + s.sourceCode[Key::VertexShader]; + s.className = metaObject()->className(); + + material->setProgramSource(s); + material->attributes = m_common.attributes; + foreach (QQuickShaderEffectNode* node, m_nodes) + node->markDirty(QSGNode::DirtyMaterial); + + m_dirtyProgram = false; + m_dirtyUniforms = true; + } + + m_lastTime = m_system->systemSync(this) / 1000.; + if (true) //Currently this is how we update timestamp... potentially over expensive. + buildData(rootNode); + return rootNode; +} + +QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() +{ +#ifdef QT_OPENGL_ES_2 + if (m_count * 4 > 0xffff) { + printf("CustomParticle: Too many particles... \n"); + return 0; + } +#endif + + if (m_count <= 0) { + printf("CustomParticle: Too few particles... \n"); + return 0; + } + + if (m_groups.isEmpty()) + return 0; + + QQuickShaderEffectNode *rootNode = 0; + QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial; + m_dirtyProgram = true; + + foreach (const QString &str, m_groups){ + int gIdx = m_system->groupIds[str]; + int count = m_system->groupData[gIdx]->size(); + + QQuickShaderEffectNode* node = new QQuickShaderEffectNode(); + m_nodes.insert(gIdx, node); + + node->setMaterial(material); + + //Create Particle Geometry + int vCount = count * 4; + int iCount = count * 6; + QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + node->setGeometry(g); + node->setFlag(QSGNode::OwnsGeometry, true); + PlainVertex *vertices = (PlainVertex *) g->vertexData(); + for (int p=0; p < count; ++p) { + commit(gIdx, p); + vertices[0].tx = 0; + vertices[0].ty = 0; + + vertices[1].tx = 1; + vertices[1].ty = 0; + + vertices[2].tx = 0; + vertices[2].ty = 1; + + vertices[3].tx = 1; + vertices[3].ty = 1; + vertices += 4; + } + quint16 *indices = g->indexDataAsUShort(); + for (int i=0; i < count; ++i) { + int o = i * 4; + indices[0] = o; + indices[1] = o + 1; + indices[2] = o + 2; + indices[3] = o + 1; + indices[4] = o + 3; + indices[5] = o + 2; + indices += 6; + } + } + + QHash<int, QQuickShaderEffectNode*>::const_iterator it = m_nodes.begin(); + rootNode = it.value(); + rootNode->setFlag(QSGNode::OwnsMaterial, true); + for (++it; it != m_nodes.end(); ++it) + rootNode->appendChildNode(it.value()); + + return rootNode; +} + +void QQuickCustomParticle::sourceDestroyed(QObject *object) +{ + m_common.sourceDestroyed(object); +} + +void QQuickCustomParticle::propertyChanged(int mappedId) +{ + bool textureProviderChanged; + m_common.propertyChanged(this, mappedId, &textureProviderChanged); + m_dirtyTextureProviders |= textureProviderChanged; + m_dirtyUniformValues = true; + update(); +} + + +void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode) +{ + if (!rootNode) + return; + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) { + if (m_common.uniformData[shaderType].at(i).name == "qt_Timestamp") + m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime); + } + } + m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()), + m_dirtyUniforms, true, m_dirtyTextureProviders); + foreach (QQuickShaderEffectNode* node, m_nodes) + node->markDirty(QSGNode::DirtyMaterial); + m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false; +} + +void QQuickCustomParticle::initialize(int gIdx, int pIdx) +{ + QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx]; + datum->r = rand()/(qreal)RAND_MAX; +} + +void QQuickCustomParticle::commit(int gIdx, int pIdx) +{ + if (m_nodes[gIdx] == 0) + return; + + QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx]; + PlainVertices *particles = (PlainVertices *) m_nodes[gIdx]->geometry()->vertexData(); + PlainVertex *vertices = (PlainVertex *)&particles[pIdx]; + for (int i=0; i<4; ++i) { + vertices[i].x = datum->x - m_systemOffset.x(); + vertices[i].y = datum->y - m_systemOffset.y(); + vertices[i].t = datum->t; + vertices[i].lifeSpan = datum->lifeSpan; + vertices[i].size = datum->size; + vertices[i].endSize = datum->endSize; + vertices[i].vx = datum->vx; + vertices[i].vy = datum->vy; + vertices[i].ax = datum->ax; + vertices[i].ay = datum->ay; + vertices[i].r = datum->r; + } +} + +void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == QQuickItem::ItemSceneChange) + m_common.updateCanvas(value.canvas); + QQuickParticlePainter::itemChange(change, value); +} + + +QT_END_NAMESPACE diff --git a/src/particles/qquickcustomparticle_p.h b/src/particles/qquickcustomparticle_p.h new file mode 100644 index 0000000000..f689091268 --- /dev/null +++ b/src/particles/qquickcustomparticle_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 CUSTOM_PARTICLE_H +#define CUSTOM_PARTICLE_H +#include "qquickparticlepainter_p.h" +#include <private/qquickshadereffectnode_p.h> +#include <private/qquickshadereffect_p.h> +#include <QSignalMapper> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGNode; +struct PlainVertices; + +class QQuickShaderEffectMaterialObject; + +//Genealogy: Hybrid of UltraParticle and ShaderEffect +class QQuickCustomParticle : public QQuickParticlePainter +{ + Q_OBJECT + Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) + Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) + +public: + explicit QQuickCustomParticle(QQuickItem* parent=0); + ~QQuickCustomParticle(); + + QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; } + void setFragmentShader(const QByteArray &code); + + QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; } + void setVertexShader(const QByteArray &code); + +Q_SIGNALS: + void fragmentShaderChanged(); + void vertexShaderChanged(); + +protected: + virtual void initialize(int gIdx, int pIdx); + virtual void commit(int gIdx, int pIdx); + + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + QQuickShaderEffectNode *prepareNextFrame(QQuickShaderEffectNode *rootNode); + void reset(); + void resize(int oldCount, int newCount); + virtual void componentComplete(); + QQuickShaderEffectNode *buildCustomNodes(); + + void sceneGraphInvalidated(); + void itemChange(ItemChange change, const ItemChangeData &value); + +private Q_SLOTS: + void sourceDestroyed(QObject *object); + void propertyChanged(int mappedId); + +private: + typedef QQuickShaderEffectMaterialKey Key; + typedef QQuickShaderEffectMaterial::UniformData UniformData; + + void buildData(QQuickShaderEffectNode *rootNode); + void updateVertexShader(); + + QQuickShaderEffectCommon m_common; + + QHash<int, QQuickShaderEffectNode*> m_nodes; + qreal m_lastTime; + + uint m_dirtyUniforms : 1; + uint m_dirtyUniformValues : 1; + uint m_dirtyTextureProviders : 1; + uint m_dirtyProgram : 1; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //HEADER_GUARD diff --git a/src/particles/qquickdirection.cpp b/src/particles/qquickdirection.cpp new file mode 100644 index 0000000000..4127d06be0 --- /dev/null +++ b/src/particles/qquickdirection.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickdirection_p.h" + +QT_BEGIN_NAMESPACE +/*! + \qmlclass Direction QQuickDirection + \inqmlmodule QtQuick.Particles 2 + \brief The Direction elements allow you to specify a vector space. + +*/ + + +QQuickDirection::QQuickDirection(QObject *parent) : + QObject(parent) +{ +} + +const QPointF QQuickDirection::sample(const QPointF &from) +{ + Q_UNUSED(from); + return QPointF(); +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickdirection_p.h b/src/particles/qquickdirection_p.h new file mode 100644 index 0000000000..651865a1f5 --- /dev/null +++ b/src/particles/qquickdirection_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 VARYINGVECTOR_H +#define VARYINGVECTOR_H + +#include <QObject> +#include <QPointF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickDirection : public QObject +{ + Q_OBJECT +public: + explicit QQuickDirection(QObject *parent = 0); + + virtual const QPointF sample(const QPointF &from); +signals: + +public slots: + +protected: +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // VARYINGVECTOR_H diff --git a/src/particles/qquickellipseextruder.cpp b/src/particles/qquickellipseextruder.cpp new file mode 100644 index 0000000000..083564e5cb --- /dev/null +++ b/src/particles/qquickellipseextruder.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickellipseextruder_p.h" +#include <stdlib.h> +#include <cmath> + +#ifdef Q_OS_QNX +#include <math.h> +#endif + +QT_BEGIN_NAMESPACE +/*! + \qmlclass EllipseShape QQuickEllipseExtruder + \inqmlmodule QtQuick.Particles 2 + \inherits Shape + \brief The EllipseShape represents an ellipse to other particle system elements + + This shape can be used by Emitter subclasses and Affector subclasses to have + them act upon an ellipse shaped area. +*/ +QQuickEllipseExtruder::QQuickEllipseExtruder(QObject *parent) : + QQuickParticleExtruder(parent) + , m_fill(true) +{ +} + +/*! + \qmlproperty bool QtQuick.Particles2::EllipseShape::fill + If fill is true the ellipse is filled; otherwise it is just a border. + + Default is true. +*/ + +QPointF QQuickEllipseExtruder::extrude(const QRectF & r) +{ + qreal theta = ((qreal)rand()/RAND_MAX) * 6.2831853071795862; + qreal mag = m_fill ? ((qreal)rand()/RAND_MAX) : 1; + return QPointF(r.x() + r.width()/2 + mag * (r.width()/2) * cos(theta), + r.y() + r.height()/2 + mag * (r.height()/2) * sin(theta)); +} + +bool QQuickEllipseExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + return bounds.contains(point);//TODO: Ellipse +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickellipseextruder_p.h b/src/particles/qquickellipseextruder_p.h new file mode 100644 index 0000000000..c2d0c634ab --- /dev/null +++ b/src/particles/qquickellipseextruder_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 ELLIPSEEXTRUDER_H +#define ELLIPSEEXTRUDER_H +#include "qquickparticleextruder_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickEllipseExtruder : public QQuickParticleExtruder +{ + Q_OBJECT + Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged)//###Use base class? If it's still box +public: + explicit QQuickEllipseExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point); + + bool fill() const + { + return m_fill; + } + +signals: + + void fillChanged(bool arg); + +public slots: + + void setFill(bool arg) + { + if (m_fill != arg) { + m_fill = arg; + emit fillChanged(arg); + } + } +private: + bool m_fill; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ELLIPSEEXTRUDER_H diff --git a/src/particles/qquickfriction.cpp b/src/particles/qquickfriction.cpp new file mode 100644 index 0000000000..d37d109f48 --- /dev/null +++ b/src/particles/qquickfriction.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickfriction_p.h" +QT_BEGIN_NAMESPACE +/*! + \qmlclass Friction QQuickFrictionAffector + \inqmlmodule QtQuick.Particles 2 + \inherits Affector + \brief The Friction affector slows down movement proportional to the particle's current speed. + +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Friction::factor + + A drag will be applied to moving objects which is this factor of their current velocity. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Friction::threshold + + The drag will only be applied to objects with a velocity above the threshold velocity. The + drag applied will bring objects down to the threshold velocity, but no further. + + The default threshold is 0 +*/ +static qreal sign(qreal a) +{ + return a >= 0 ? 1 : -1; +} + +static const qreal epsilon = 0.00001; + +QQuickFrictionAffector::QQuickFrictionAffector(QQuickItem *parent) : + QQuickParticleAffector(parent), m_factor(0.0), m_threshold(0.0) +{ +} + +bool QQuickFrictionAffector::affectParticle(QQuickParticleData *d, qreal dt) +{ + if (!m_factor) + return false; + qreal curVX = d->curVX(); + qreal curVY = d->curVY(); + if (!curVX && !curVY) + return false; + qreal newVX = curVX + (curVX * m_factor * -1 * dt); + qreal newVY = curVY + (curVY * m_factor * -1 * dt); + + if (!m_threshold) { + if (sign(curVX) != sign(newVX)) + newVX = 0; + if (sign(curVY) != sign(newVY)) + newVY = 0; + } else { + qreal curMag = sqrt(curVX*curVX + curVY*curVY); + if (curMag <= m_threshold + epsilon) + return false; + qreal newMag = sqrt(newVX*newVX + newVY*newVY); + if (newMag <= m_threshold + epsilon || //went past the threshold, stop there instead + sign(curVX) != sign(newVX) || //went so far past maybe it came out the other side! + sign(curVY) != sign(newVY)) { + qreal theta = atan2(curVY, curVX); + newVX = m_threshold * cos(theta); + newVY = m_threshold * sin(theta); + } + } + + d->setInstantaneousVX(newVX); + d->setInstantaneousVY(newVY); + return true; +} +QT_END_NAMESPACE diff --git a/src/particles/qquickfriction_p.h b/src/particles/qquickfriction_p.h new file mode 100644 index 0000000000..3b06710529 --- /dev/null +++ b/src/particles/qquickfriction_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 FRICTIONAFFECTOR_H +#define FRICTIONAFFECTOR_H +#include "qquickparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickFrictionAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal factor READ factor WRITE setFactor NOTIFY factorChanged) + Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged) +public: + explicit QQuickFrictionAffector(QQuickItem *parent = 0); + + qreal factor() const + { + return m_factor; + } + + qreal threshold() const + { + return m_threshold; + } + +protected: + virtual bool affectParticle(QQuickParticleData *d, qreal dt); + +signals: + + void factorChanged(qreal arg); + void thresholdChanged(qreal arg); + +public slots: + + void setFactor(qreal arg) + { + if (m_factor != arg) { + m_factor = arg; + emit factorChanged(arg); + } + } + + void setThreshold(qreal arg) + { + if (m_threshold != arg) { + m_threshold = arg; + emit thresholdChanged(arg); + } + } + +private: + qreal m_factor; + qreal m_threshold; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // FRICTIONAFFECTOR_H diff --git a/src/particles/qquickgravity.cpp b/src/particles/qquickgravity.cpp new file mode 100644 index 0000000000..cf4f35eb72 --- /dev/null +++ b/src/particles/qquickgravity.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickgravity_p.h" +#include <cmath> +QT_BEGIN_NAMESPACE +const qreal CONV = 0.017453292520444443; +/*! + \qmlclass Gravity QQuickGravityAffector + \inqmlmodule QtQuick.Particles 2 + \inherits Affector + \brief The Gravity element allows you to set an accleration in an angle + + This element will accelerate all affected particles to a vector of + the specified magnitude in the specified angle. If the angle and acceleration do + not vary, it is more efficient to set the specified acceleration on the Emitter. + + This element models the gravity of a massive object whose center of + gravity is far away (and thus the gravitational pull is effectively constant + across the scene). To model the gravity of an object near or inside the scene, + use PointAttractor. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Gravity::magnitude + + Pixels per second that objects will be accelerated by. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Gravity::acceleration + + Name changed to magnitude, will be removed soon. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Gravity::angle + + Angle of acceleration. +*/ + +QQuickGravityAffector::QQuickGravityAffector(QQuickItem *parent) : + QQuickParticleAffector(parent), m_magnitude(-10), m_angle(90), m_needRecalc(true) +{ +} + +bool QQuickGravityAffector::affectParticle(QQuickParticleData *d, qreal dt) +{ + if (!m_magnitude) + return false; + if (m_needRecalc) { + m_needRecalc = false; + m_dx = m_magnitude * cos(m_angle * CONV); + m_dy = m_magnitude * sin(m_angle * CONV); + } + + d->setInstantaneousVX(d->curVX() + m_dx*dt); + d->setInstantaneousVY(d->curVY() + m_dy*dt); + return true; +} +QT_END_NAMESPACE diff --git a/src/particles/qquickgravity_p.h b/src/particles/qquickgravity_p.h new file mode 100644 index 0000000000..a738fd3ef3 --- /dev/null +++ b/src/particles/qquickgravity_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 GRAVITYAFFECTOR_H +#define GRAVITYAFFECTOR_H +#include "qquickparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickGravityAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged) + Q_PROPERTY(qreal acceleration READ magnitude WRITE setAcceleration NOTIFY magnitudeChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) +public: + explicit QQuickGravityAffector(QQuickItem *parent = 0); + qreal magnitude() const + { + return m_magnitude; + } + + qreal angle() const + { + return m_angle; + } +protected: + virtual bool affectParticle(QQuickParticleData *d, qreal dt); +signals: + + void magnitudeChanged(qreal arg); + + void angleChanged(qreal arg); + +public slots: +void setAcceleration(qreal arg) +{ + qWarning() << "Gravity::acceleration has been renamed Gravity::magnitude"; + if (m_magnitude != arg) { + m_magnitude = arg; + m_needRecalc = true; + emit magnitudeChanged(arg); + } +} + +void setMagnitude(qreal arg) +{ + if (m_magnitude != arg) { + m_magnitude = arg; + m_needRecalc = true; + emit magnitudeChanged(arg); + } +} + +void setAngle(qreal arg) +{ + if (m_angle != arg) { + m_angle = arg; + m_needRecalc = true; + emit angleChanged(arg); + } +} + +private: + qreal m_magnitude; + qreal m_angle; + + bool m_needRecalc; + qreal m_dx; + qreal m_dy; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // GRAVITYAFFECTOR_H diff --git a/src/particles/qquickgroupgoal.cpp b/src/particles/qquickgroupgoal.cpp new file mode 100644 index 0000000000..0d7f15a9e1 --- /dev/null +++ b/src/particles/qquickgroupgoal.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickgroupgoal_p.h" +#include <private/qquickspriteengine_p.h> +#include <private/qquicksprite_p.h> +#include "qquickimageparticle_p.h" +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass GroupGoal QQuickGroupGoalAffector + \inqmlmodule QtQuick.Particles 2 + \inherits Affector + \brief The GroupGoal Affector allows you to change the state of a group of a particle. + +*/ +/*! + \qmlproperty string QtQuick.Particles2::GroupGoal::goalState + + The name of the group which the affected particles should move to. + + Groups can have defined durations and transitions between them, setting goalState + will cause it to disregard any path weightings (including 0) and head down the path + which will reach the goalState quickest. It will pass through intermediate groups + on that path for their respective durations. +*/ +/*! + \qmlproperty bool QtQuick.Particles2::GroupGoal::jump + + If true, affected particles will jump directly to the target group instead of taking the + the shortest valid path to get there. They will also not finish their current state, + but immediately move to the beginning of the goal state. + + Default is false. +*/ + +QQuickGroupGoalAffector::QQuickGroupGoalAffector(QQuickItem *parent) : + QQuickParticleAffector(parent), m_jump(false) +{ + m_ignoresTime = true; +} + +void QQuickGroupGoalAffector::setGoalState(QString arg) +{ + if (m_goalState != arg) { + m_goalState = arg; + emit goalStateChanged(arg); + } +} + +bool QQuickGroupGoalAffector::affectParticle(QQuickParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + QQuickStochasticEngine *engine = m_system->stateEngine; + bool notUsingEngine = false; + if (!engine) + notUsingEngine = true; + + int index = d->systemIndex; + int goalIdx = m_system->groupIds[m_goalState]; + if (notUsingEngine){//no stochastic states defined. So cut out the engine + //TODO: It's possible to move to a group that is intermediate and not used by painters or emitters - but right now that will redirect to the default group + m_system->moveGroups(d, goalIdx); + return true; + }else if (engine->curState(index) != goalIdx){ + engine->setGoal(goalIdx, index, m_jump); + return true; + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickgroupgoal_p.h b/src/particles/qquickgroupgoal_p.h new file mode 100644 index 0000000000..f553badbd6 --- /dev/null +++ b/src/particles/qquickgroupgoal_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 GROUPGOALAFFECTOR_H +#define GROUPGOALAFFECTOR_H +#include "qquickparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickStochasticEngine; + +class QQuickGroupGoalAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged) + Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged) +public: + explicit QQuickGroupGoalAffector(QQuickItem *parent = 0); + + QString goalState() const + { + return m_goalState; + } + + bool jump() const + { + return m_jump; + } + +protected: + virtual bool affectParticle(QQuickParticleData *d, qreal dt); + +signals: + + void goalStateChanged(QString arg); + + void jumpChanged(bool arg); + +public slots: + + void setGoalState(QString arg); + + void setJump(bool arg) + { + if (m_jump != arg) { + m_jump = arg; + emit jumpChanged(arg); + } + } + +private: + QString m_goalState; + bool m_jump; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // GROUPGOALAFFECTOR_H diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp new file mode 100644 index 0000000000..d9eb6ed01b --- /dev/null +++ b/src/particles/qquickimageparticle.cpp @@ -0,0 +1,1978 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 <QtQuick/private/qsgcontext_p.h> +#include <private/qsgadaptationlayer_p.h> +#include <private/qquickitem_p.h> +#include <QtQuick/qsgnode.h> +#include <QtQuick/qsgtexturematerial.h> +#include <QtQuick/qsgtexture.h> +#include <QFile> +#include "qquickimageparticle_p.h" +#include "qquickparticleemitter_p.h" +#include <private/qquicksprite_p.h> +#include <private/qquickspriteengine_p.h> +#include <QOpenGLFunctions> +#include <QtQuick/qsgengine.h> +#include <QtQuick/private/qsgtexture_p.h> +#include <private/qqmlglobal_p.h> +#include <QtQml/qqmlinfo.h> +#include <cmath> + +QT_BEGIN_NAMESPACE + +#ifndef QT_OPENGL_ES_2 +#define SHADER_DEFINES "#version 120\n" +#else +#define SHADER_DEFINES "" +#endif + +//TODO: Make it larger on desktop? Requires fixing up shader code with the same define +#define UNIFORM_ARRAY_SIZE 64 + +static const char vertexShaderCode[] = + "#if defined(DEFORM)\n" + "attribute highp vec4 vPosTex;\n" + "#else\n" + "attribute highp vec2 vPos;\n" + "#endif\n" + "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize\n" + "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration\n" + "uniform highp float entry;\n" + "#if defined(COLOR)\n" + "attribute highp vec4 vColor;\n" + "#endif\n" + "#if defined(DEFORM)\n" + "attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector\n" + "attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate\n" + "#endif\n" + "#if defined(SPRITE)\n" + "attribute highp vec3 vAnimData;// w,h(premultiplied of anim), interpolation progress\n" + "attribute highp vec4 vAnimPos;//x,y, x,y (two frames for interpolation)\n" + "#endif\n" + "\n" + "uniform highp mat4 qt_Matrix;\n" + "uniform highp float timestamp;\n" + "#if defined(TABLE)\n" + "varying lowp vec2 tt;//y is progress if Sprite mode\n" + "uniform highp float sizetable[64];\n" + "uniform highp float opacitytable[64];\n" + "#endif\n" + "#if defined(SPRITE)\n" + "varying highp vec4 fTexS;\n" + "#elif defined(DEFORM)\n" + "varying highp vec2 fTex;\n" + "#endif\n" + "#if defined(COLOR)\n" + "varying lowp vec4 fColor;\n" + "#else\n" + "varying lowp float fFade;\n" + "#endif\n" + "\n" + "\n" + "void main() {\n" + "\n" + " highp float t = (timestamp - vData.x) / vData.y;\n" + " if (t < 0. || t > 1.) {\n" + "#if defined(DEFORM)\n" + " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n" + "#else\n" + " gl_PointSize = 0.;\n" + "#endif\n" + " } else {\n" + "#if defined(SPRITE)\n" + " tt.y = vAnimData.z;\n" + " //Calculate frame location in texture\n" + " fTexS.xy = vAnimPos.xy + vPosTex.zw * vAnimData.xy;\n" + " //Next frame is also passed, for interpolation\n" + " fTexS.zw = vAnimPos.zw + vPosTex.zw * vAnimData.xy;\n" + "\n" + "#elif defined(DEFORM)\n" + " fTex = vPosTex.zw;\n" + "#endif\n" + " highp float currentSize = mix(vData.z, vData.w, t * t);\n" + " lowp float fade = 1.;\n" + " highp float fadeIn = min(t * 10., 1.);\n" + " highp float fadeOut = 1. - clamp((t - 0.75) * 4.,0., 1.);\n" + "\n" + "#if defined(TABLE)\n" + " currentSize = currentSize * sizetable[int(floor(t*64.))];\n" + " fade = fade * opacitytable[int(floor(t*64.))];\n" + "#endif\n" + "\n" + " if (entry == 1.)\n" + " fade = fade * fadeIn * fadeOut;\n" + " else if (entry == 2.)\n" + " currentSize = currentSize * fadeIn * fadeOut;\n" + "\n" + " if (currentSize <= 0.) {\n" + "#if defined(DEFORM)\n" + " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n" + "#else\n" + " gl_PointSize = 0.;\n" + "#endif\n" + " } else {\n" + " if (currentSize < 3.)//Sizes too small look jittery as they move\n" + " currentSize = 3.;\n" + "\n" + " highp vec2 pos;\n" + "#if defined(DEFORM)\n" + " highp float rotation = vRotation.x + vRotation.y * t * vData.y;\n" + " if (vRotation.z == 1.0){\n" + " highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy;\n" + " rotation += atan(curVel.y, curVel.x);\n" + " }\n" + " highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation));\n" + " highp vec4 deform = vDeformVec * currentSize * (vPosTex.zzww - 0.5);\n" + " highp vec4 rotatedDeform = deform.xxzz * trigCalcs.xyxy;\n" + " rotatedDeform = rotatedDeform + (deform.yyww * trigCalcs.yxyx * vec4(-1.,1.,-1.,1.));\n" + " /* The readable version:\n" + " highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5);\n" + " highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5);\n" + " highp vec2 xRotatedDeform;\n" + " xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y;\n" + " xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y;\n" + " highp vec2 yRotatedDeform;\n" + " yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y;\n" + " yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y;\n" + " */\n" + " pos = vPosTex.xy\n" + " + rotatedDeform.xy\n" + " + rotatedDeform.zw\n" + " + vVec.xy * t * vData.y // apply speed\n" + " + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration\n" + "#else\n" + " pos = vPos\n" + " + vVec.xy * t * vData.y // apply speed vector..\n" + " + 0.5 * vVec.zw * pow(t * vData.y, 2.);\n" + " gl_PointSize = currentSize;\n" + "#endif\n" + " gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n" + "\n" + "#if defined(COLOR)\n" + " fColor = vColor * fade;\n" + "#else\n" + " fFade = fade;\n" + "#endif\n" + "#if defined(TABLE)\n" + " tt.x = t;\n" + "#endif\n" + " }\n" + " }\n" + "}\n"; + +static const char fragmentShaderCode[] = + "uniform sampler2D texture;\n" + "uniform lowp float qt_Opacity;\n" + "\n" + "#if defined(SPRITE)\n" + "varying highp vec4 fTexS;\n" + "#elif defined(DEFORM)\n" + "varying highp vec2 fTex;\n" + "#endif\n" + "#if defined(COLOR)\n" + "varying lowp vec4 fColor;\n" + "#else\n" + "varying lowp float fFade;\n" + "#endif\n" + "#if defined(TABLE)\n" + "varying lowp vec2 tt;\n" + "uniform sampler2D colortable;\n" + "#endif\n" + "\n" + "void main() {\n" + "#if defined(SPRITE)\n" + " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), tt.y)\n" + " * fColor\n" + " * texture2D(colortable, tt)\n" + " * qt_Opacity;\n" + "#elif defined(TABLE)\n" + " gl_FragColor = texture2D(texture, fTex)\n" + " * fColor\n" + " * texture2D(colortable, tt)\n" + " * qt_Opacity;\n" + "#elif defined(DEFORM)\n" + " gl_FragColor = (texture2D(texture, fTex)) * fColor * qt_Opacity;\n" + "#elif defined(COLOR)\n" + " gl_FragColor = (texture2D(texture, gl_PointCoord)) * fColor * qt_Opacity;\n" + "#else\n" + " gl_FragColor = texture2D(texture, gl_PointCoord) * (fFade * qt_Opacity);\n" + "#endif\n" + "}\n"; + +const qreal CONV = 0.017453292519943295; +class ImageMaterialData +{ + public: + ImageMaterialData() + : texture(0), colorTable(0) + {} + + ~ImageMaterialData(){ + delete texture; + delete colorTable; + } + + QSGTexture *texture; + QSGTexture *colorTable; + float sizeTable[UNIFORM_ARRAY_SIZE]; + float opacityTable[UNIFORM_ARRAY_SIZE]; + + qreal timestamp; + qreal entry; + QSizeF animSheetSize; +}; + +class TabledMaterialData : public ImageMaterialData {}; +class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData> +{ + QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData) + +public: + TabledMaterial() + { + m_vertex_code = QByteArray(SHADER_DEFINES) + + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n") + + vertexShaderCode; + + m_fragment_code = QByteArray(SHADER_DEFINES) + + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n") + + fragmentShaderCode; + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + const char *vertexShader() const { return m_vertex_code.constData(); } + const char *fragmentShader() const { return m_fragment_code.constData(); } + + QList<QByteArray> attributes() const { + return QList<QByteArray>() << "vPosTex" << "vData" << "vVec" + << "vColor" << "vDeformVec" << "vRotation"; + }; + + void initialize() { + QSGSimpleMaterialShader<TabledMaterialData>::initialize(); + program()->bind(); + program()->setUniformValue("texture", 0); + program()->setUniformValue("colortable", 1); + glFuncs = QOpenGLContext::currentContext()->functions(); + m_timestamp_id = program()->uniformLocation("timestamp"); + m_entry_id = program()->uniformLocation("entry"); + m_sizetable_id = program()->uniformLocation("sizetable"); + m_opacitytable_id = program()->uniformLocation("opacitytable"); + } + + void updateState(const TabledMaterialData* d, const TabledMaterialData*) { + glFuncs->glActiveTexture(GL_TEXTURE1); + d->colorTable->bind(); + + glFuncs->glActiveTexture(GL_TEXTURE0); + d->texture->bind(); + + program()->setUniformValue(m_timestamp_id, (float) d->timestamp); + program()->setUniformValue(m_entry_id, (float) d->entry); + program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1); + program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1); + } + + int m_entry_id; + int m_timestamp_id; + int m_sizetable_id; + int m_opacitytable_id; + QByteArray m_vertex_code; + QByteArray m_fragment_code; + QOpenGLFunctions* glFuncs; +}; + +class DeformableMaterialData : public ImageMaterialData {}; +class DeformableMaterial : public QSGSimpleMaterialShader<DeformableMaterialData> +{ + QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData) + +public: + DeformableMaterial() + { + m_vertex_code = QByteArray(SHADER_DEFINES) + + QByteArray("#define DEFORM\n#define COLOR\n") + + vertexShaderCode; + + m_fragment_code = QByteArray(SHADER_DEFINES) + + QByteArray("#define DEFORM\n#define COLOR\n") + + fragmentShaderCode; + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + const char *vertexShader() const { return m_vertex_code.constData(); } + const char *fragmentShader() const { return m_fragment_code.constData(); } + + QList<QByteArray> attributes() const { + return QList<QByteArray>() << "vPosTex" << "vData" << "vVec" + << "vColor" << "vDeformVec" << "vRotation"; + }; + + void initialize() { + QSGSimpleMaterialShader<DeformableMaterialData>::initialize(); + program()->bind(); + program()->setUniformValue("texture", 0); + glFuncs = QOpenGLContext::currentContext()->functions(); + m_timestamp_id = program()->uniformLocation("timestamp"); + m_entry_id = program()->uniformLocation("entry"); + } + + void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) { + glFuncs->glActiveTexture(GL_TEXTURE0); + d->texture->bind(); + + program()->setUniformValue(m_timestamp_id, (float) d->timestamp); + program()->setUniformValue(m_entry_id, (float) d->entry); + } + + int m_entry_id; + int m_timestamp_id; + QByteArray m_vertex_code; + QByteArray m_fragment_code; + QOpenGLFunctions* glFuncs; +}; + +class SpriteMaterialData : public ImageMaterialData {}; +class SpriteMaterial : public QSGSimpleMaterialShader<SpriteMaterialData> +{ + QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData) + +public: + SpriteMaterial() + { + m_vertex_code = QByteArray(SHADER_DEFINES) + + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n") + + vertexShaderCode; + + m_fragment_code = QByteArray(SHADER_DEFINES) + + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n") + + fragmentShaderCode; + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + const char *vertexShader() const { return m_vertex_code.constData(); } + const char *fragmentShader() const { return m_fragment_code.constData(); } + + QList<QByteArray> attributes() const { + return QList<QByteArray>() << "vPosTex" << "vData" << "vVec" + << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos"; + }; + + void initialize() { + QSGSimpleMaterialShader<SpriteMaterialData>::initialize(); + program()->bind(); + program()->setUniformValue("texture", 0); + program()->setUniformValue("colortable", 1); + glFuncs = QOpenGLContext::currentContext()->functions(); + //Don't actually expose the animSheetSize in the shader, it's currently only used for CPU calculations. + m_timestamp_id = program()->uniformLocation("timestamp"); + m_entry_id = program()->uniformLocation("entry"); + m_sizetable_id = program()->uniformLocation("sizetable"); + m_opacitytable_id = program()->uniformLocation("opacitytable"); + } + + void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) { + glFuncs->glActiveTexture(GL_TEXTURE1); + d->colorTable->bind(); + + // make sure we end by setting GL_TEXTURE0 as active texture + glFuncs->glActiveTexture(GL_TEXTURE0); + d->texture->bind(); + + program()->setUniformValue(m_timestamp_id, (float) d->timestamp); + program()->setUniformValue(m_entry_id, (float) d->entry); + program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, 64, 1); + program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1); + } + + int m_timestamp_id; + int m_entry_id; + int m_sizetable_id; + int m_opacitytable_id; + QByteArray m_vertex_code; + QByteArray m_fragment_code; + QOpenGLFunctions* glFuncs; +}; + +class ColoredMaterialData : public ImageMaterialData {}; +class ColoredMaterial : public QSGSimpleMaterialShader<ColoredMaterialData> +{ + QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData) + +public: + ColoredMaterial() + { + m_vertex_code = QByteArray(SHADER_DEFINES) + + QByteArray("#define COLOR\n") + + vertexShaderCode; + + m_fragment_code = QByteArray(SHADER_DEFINES) + + QByteArray("#define COLOR\n") + + fragmentShaderCode; + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + const char *vertexShader() const { return m_vertex_code.constData(); } + const char *fragmentShader() const { return m_fragment_code.constData(); } + + void activate() { + QSGSimpleMaterialShader<ColoredMaterialData>::activate(); +#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN) + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); +#endif + } + + void deactivate() { + QSGSimpleMaterialShader<ColoredMaterialData>::deactivate(); +#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN) + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); +#endif + } + + QList<QByteArray> attributes() const { + return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor"; + } + + void initialize() { + QSGSimpleMaterialShader<ColoredMaterialData>::initialize(); + program()->bind(); + program()->setUniformValue("texture", 0); + glFuncs = QOpenGLContext::currentContext()->functions(); + m_timestamp_id = program()->uniformLocation("timestamp"); + m_entry_id = program()->uniformLocation("entry"); + } + + void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) { + glFuncs->glActiveTexture(GL_TEXTURE0); + d->texture->bind(); + + program()->setUniformValue(m_timestamp_id, (float) d->timestamp); + program()->setUniformValue(m_entry_id, (float) d->entry); + } + + int m_timestamp_id; + int m_entry_id; + QByteArray m_vertex_code; + QByteArray m_fragment_code; + QOpenGLFunctions* glFuncs; +}; + +class SimpleMaterialData : public ImageMaterialData {}; +class SimpleMaterial : public QSGSimpleMaterialShader<SimpleMaterialData> +{ + QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData) + +public: + SimpleMaterial() + { + m_vertex_code = QByteArray(SHADER_DEFINES) + + vertexShaderCode; + + m_fragment_code = QByteArray(SHADER_DEFINES) + + fragmentShaderCode; + + Q_ASSERT(!m_vertex_code.isNull()); + Q_ASSERT(!m_fragment_code.isNull()); + } + + const char *vertexShader() const { return m_vertex_code.constData(); } + const char *fragmentShader() const { return m_fragment_code.constData(); } + + void activate() { + QSGSimpleMaterialShader<SimpleMaterialData>::activate(); +#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN) + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); +#endif + } + + void deactivate() { + QSGSimpleMaterialShader<SimpleMaterialData>::deactivate(); +#if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN) + glDisable(GL_POINT_SPRITE); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); +#endif + } + + QList<QByteArray> attributes() const { + return QList<QByteArray>() << "vPos" << "vData" << "vVec"; + } + + void initialize() { + QSGSimpleMaterialShader<SimpleMaterialData>::initialize(); + program()->bind(); + program()->setUniformValue("texture", 0); + glFuncs = QOpenGLContext::currentContext()->functions(); + m_timestamp_id = program()->uniformLocation("timestamp"); + m_entry_id = program()->uniformLocation("entry"); + } + + void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) { + glFuncs->glActiveTexture(GL_TEXTURE0); + d->texture->bind(); + + program()->setUniformValue(m_timestamp_id, (float) d->timestamp); + program()->setUniformValue(m_entry_id, (float) d->entry); + } + + int m_timestamp_id; + int m_entry_id; + QByteArray m_vertex_code; + QByteArray m_fragment_code; + QOpenGLFunctions* glFuncs; +}; + +void fillUniformArrayFromImage(float* array, const QImage& img, int size) +{ + if (img.isNull()){ + for (int i=0; i<size; i++) + array[i] = 1.0; + return; + } + QImage scaled = img.scaled(size,1); + for (int i=0; i<size; i++) + array[i] = qAlpha(scaled.pixel(i,0))/255.0; +} + +/*! + \qmlclass ImageParticle QQuickImageParticle + \inqmlmodule QtQuick.Particles 2 + \inherits ParticlePainter + \brief The ImageParticle element visualizes logical particles using an image + + This element renders a logical particle as an image. The image can be + \list + \li colorized + \li rotated + \li deformed + \li a sprite-based animation + \endlist + + ImageParticles implictly share data on particles if multiple ImageParticles are painting + the same logical particle group. This is broken down along the four capabilities listed + above. So if one ImageParticle defines data for rendering the particles in one of those + capabilities, and the other does not, then both will draw the particles the same in that + aspect automatically. This is primarily useful when there is some random variation on + the particle which is supposed to stay with it when switching painters. If both ImageParticles + define how they should appear for that aspect, they diverge and each appears as it is defined. + + This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly + set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole. + So if you explicity set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation + to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from + other ImageParticles. +*/ +/*! + \qmlproperty url QtQuick.Particles2::ImageParticle::source + + The source image to be used. + + If the image is a sprite animation, use the sprite property instead. +*/ +/*! + \qmlproperty list<Sprite> QtQuick.Particles2::ImageParticle::sprites + + The sprite or sprites used to draw this particle. + + Note that the sprite image will be scaled to a square based on the size of + the particle being rendered. +*/ +/*! + \qmlproperty url QtQuick.Particles2::ImageParticle::colorTable + + An image whose color will be used as a 1D texture to determine color over life. E.g. when + the particle is halfway through its lifetime, it will have the color specified halfway + across the image. + + This color is blended with the color property and the color of the source image. +*/ +/*! + \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable + + An image whose opacity will be used as a 1D texture to determine size over life. + + This property is expected to be removed shortly, in favor of custom easing curves to determine size over life. +*/ +/*! + \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable + + An image whose opacity will be used as a 1D texture to determine size over life. + + This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life. +*/ +/*! + \qmlproperty color QtQuick.Particles2::ImageParticle::color + + If a color is specified, the provided image will be colorized with it. + + Default is white (no change). +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::colorVariation + + This number represents the color variation applied to individual particles. + Setting colorVariation is the same as setting redVariation, greenVariation, + and blueVariation to the same number. + + Each channel can vary between particle by up to colorVariation from its usual color. + + Color is measured, per channel, from 0.0 to 1.0. + + Default is 0.0 +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::redVariation + The variation in the red color channel between particles. + + Color is measured, per channel, from 0.0 to 1.0. + + Default is 0.0 +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::greenVariation + The variation in the green color channel between particles. + + Color is measured, per channel, from 0.0 to 1.0. + + Default is 0.0 +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::blueVariation + The variation in the blue color channel between particles. + + Color is measured, per channel, from 0.0 to 1.0. + + Default is 0.0 +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::alpha + An alpha to be applied to the image. This value is multiplied by the value in + the image, and the value in the color property. + + Particles have additive blending, so lower alpha on single particles leads + to stronger effects when multiple particles overlap. + + Alpha is measured from 0.0 to 1.0. + + Default is 1.0 +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::alphaVariation + The variation in the alpha channel between particles. + + Alpha is measured from 0.0 to 1.0. + + Default is 0.0 +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::rotation + + If set the image will be rotated by this many degrees before it is drawn. + + The particle coordinates are not transformed. +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::rotationVariation + + If set the rotation of individual particles will vary by up to this much + between particles. + +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeed + + If set particles will rotate at this speed in degrees/second. +*/ +/*! + \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeedVariation + + If set the rotationSpeed of individual particles will vary by up to this much + between particles. + +*/ +/*! + \qmlproperty bool QtQuick.Particles2::ImageParticle::autoRotation + + If set to true then a rotation will be applied on top of the particles rotation, so + that it faces the direction of travel. So to face away from the direction of travel, + set autoRotation to true and rotation to 180. + + Default is false +*/ +/*! + \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::xVector + + Allows you to deform the particle image when drawn. The rectangular image will + be deformed so that the horizontal sides are in the shape of this vector instead + of (1,0). +*/ +/*! + \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::yVector + + Allows you to deform the particle image when drawn. The rectangular image will + be deformed so that the vertical sides are in the shape of this vector instead + of (0,1). +*/ +/*! + \qmlproperty EntryEffect QtQuick.Particles2::ImageParticle::entryEffect + + This property provides basic and cheap entrance and exit effects for the particles. + For fine-grained control, see sizeTable and opacityTable. + + Acceptable values are + \list + \li ImageParticle.None: Particles just appear and disappear. + \li ImageParticle.Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end. + \li ImageParticle.Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end. + \endlist + + Default value is Fade. +*/ +/*! + \qmlproperty bool QtQuick.Particles2::ImageParticle::spritesInterpolate + + If set to true, sprite particles will interpolate between sprite frames each rendered frame, making + the sprites look smoother. + + Default is true. +*/ + +/*! + \qmlproperty Status QtQuick.Particles2::ImageParticle::status + + The status of loading the image. +*/ + + +QQuickImageParticle::QQuickImageParticle(QQuickItem* parent) + : QQuickParticlePainter(parent) + , m_image(0) + , m_colorTable(0) + , m_sizeTable(0) + , m_opacityTable(0) + , m_color_variation(0.0) + , m_rootNode(0) + , m_material(0) + , m_alphaVariation(0.0) + , m_alpha(1.0) + , m_redVariation(0.0) + , m_greenVariation(0.0) + , m_blueVariation(0.0) + , m_rotation(0) + , m_rotationVariation(0) + , m_rotationSpeed(0) + , m_rotationSpeedVariation(0) + , m_autoRotation(false) + , m_xVector(0) + , m_yVector(0) + , m_spriteEngine(0) + , m_spritesInterpolate(true) + , m_explicitColor(false) + , m_explicitRotation(false) + , m_explicitDeformation(false) + , m_explicitAnimation(false) + , m_bypassOptimizations(false) + , perfLevel(Unknown) + , m_lastLevel(Unknown) + , m_debugMode(false) + , m_entryEffect(Fade) + , m_buildingNodes(false) +{ + setFlag(ItemHasContents); +} + +QQuickImageParticle::~QQuickImageParticle() +{ +} + +QQmlListProperty<QQuickSprite> QQuickImageParticle::sprites() +{ + return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); +} + +void QQuickImageParticle::sceneGraphInvalidated() +{ + m_nodes.clear(); + m_rootNode = 0; + m_material = 0; +} + +void QQuickImageParticle::setImage(const QUrl &image) +{ + if (image.isEmpty()){ + if (m_image) { + delete m_image; + emit imageChanged(); + } + return; + } + + if (!m_image) + m_image = new ImageData; + if (image == m_image->source) + return; + m_image->source = image; + emit imageChanged(); + reset(); +} + + +void QQuickImageParticle::setColortable(const QUrl &table) +{ + if (table.isEmpty()){ + if (m_colorTable) { + delete m_colorTable; + emit colortableChanged(); + } + return; + } + + if (!m_colorTable) + m_colorTable = new ImageData; + if (table == m_colorTable->source) + return; + m_colorTable->source = table; + emit colortableChanged(); + reset(); +} + +void QQuickImageParticle::setSizetable(const QUrl &table) +{ + if (table.isEmpty()){ + if (m_sizeTable) { + delete m_sizeTable; + emit sizetableChanged(); + } + return; + } + + if (!m_sizeTable) + m_sizeTable = new ImageData; + if (table == m_sizeTable->source) + return; + m_sizeTable->source = table; + emit sizetableChanged(); + reset(); +} + +void QQuickImageParticle::setOpacitytable(const QUrl &table) +{ + if (table.isEmpty()){ + if (m_opacityTable) { + delete m_opacityTable; + emit opacitytableChanged(); + } + return; + } + + if (!m_opacityTable) + m_opacityTable = new ImageData; + if (table == m_opacityTable->source) + return; + m_opacityTable->source = table; + emit opacitytableChanged(); + reset(); +} + +void QQuickImageParticle::setColor(const QColor &color) +{ + if (color == m_color) + return; + m_color = color; + emit colorChanged(); + m_explicitColor = true; + if (perfLevel < Colored) + reset(); +} + +void QQuickImageParticle::setColorVariation(qreal var) +{ + if (var == m_color_variation) + return; + m_color_variation = var; + emit colorVariationChanged(); + m_explicitColor = true; + if (perfLevel < Colored) + reset(); +} + +void QQuickImageParticle::setAlphaVariation(qreal arg) +{ + if (m_alphaVariation != arg) { + m_alphaVariation = arg; + emit alphaVariationChanged(arg); + } + m_explicitColor = true; + if (perfLevel < Colored) + reset(); +} + +void QQuickImageParticle::setAlpha(qreal arg) +{ + if (m_alpha != arg) { + m_alpha = arg; + emit alphaChanged(arg); + } + m_explicitColor = true; + if (perfLevel < Colored) + reset(); +} + +void QQuickImageParticle::setRedVariation(qreal arg) +{ + if (m_redVariation != arg) { + m_redVariation = arg; + emit redVariationChanged(arg); + } + m_explicitColor = true; + if (perfLevel < Colored) + reset(); +} + +void QQuickImageParticle::setGreenVariation(qreal arg) +{ + if (m_greenVariation != arg) { + m_greenVariation = arg; + emit greenVariationChanged(arg); + } + m_explicitColor = true; + if (perfLevel < Colored) + reset(); +} + +void QQuickImageParticle::setBlueVariation(qreal arg) +{ + if (m_blueVariation != arg) { + m_blueVariation = arg; + emit blueVariationChanged(arg); + } + m_explicitColor = true; + if (perfLevel < Colored) + reset(); +} + +void QQuickImageParticle::setRotation(qreal arg) +{ + if (m_rotation != arg) { + m_rotation = arg; + emit rotationChanged(arg); + } + m_explicitRotation = true; + if (perfLevel < Deformable) + reset(); +} + +void QQuickImageParticle::setRotationVariation(qreal arg) +{ + if (m_rotationVariation != arg) { + m_rotationVariation = arg; + emit rotationVariationChanged(arg); + } + m_explicitRotation = true; + if (perfLevel < Deformable) + reset(); +} + +void QQuickImageParticle::setRotationSpeed(qreal arg) +{ + if (m_rotationSpeed != arg) { + m_rotationSpeed = arg; + emit rotationSpeedChanged(arg); + } + m_explicitRotation = true; + if (perfLevel < Deformable) + reset(); +} + +void QQuickImageParticle::setRotationSpeedVariation(qreal arg) +{ + if (m_rotationSpeedVariation != arg) { + m_rotationSpeedVariation = arg; + emit rotationSpeedVariationChanged(arg); + } + m_explicitRotation = true; + if (perfLevel < Deformable) + reset(); +} + +void QQuickImageParticle::setAutoRotation(bool arg) +{ + if (m_autoRotation != arg) { + m_autoRotation = arg; + emit autoRotationChanged(arg); + } + m_explicitRotation = true; + if (perfLevel < Deformable) + reset(); +} + +void QQuickImageParticle::setXVector(QQuickDirection* arg) +{ + if (m_xVector != arg) { + m_xVector = arg; + emit xVectorChanged(arg); + } + m_explicitDeformation = true; + if (perfLevel < Deformable) + reset(); +} + +void QQuickImageParticle::setYVector(QQuickDirection* arg) +{ + if (m_yVector != arg) { + m_yVector = arg; + emit yVectorChanged(arg); + } + m_explicitDeformation = true; + if (perfLevel < Deformable) + reset(); +} + +void QQuickImageParticle::setSpritesInterpolate(bool arg) +{ + if (m_spritesInterpolate != arg) { + m_spritesInterpolate = arg; + emit spritesInterpolateChanged(arg); + } +} + +void QQuickImageParticle::setBypassOptimizations(bool arg) +{ + if (m_bypassOptimizations != arg) { + m_bypassOptimizations = arg; + emit bypassOptimizationsChanged(arg); + } + if (perfLevel < 9999) + reset(); +} + +void QQuickImageParticle::setEntryEffect(EntryEffect arg) +{ + if (m_entryEffect != arg) { + m_entryEffect = arg; + if (m_material) + getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect; + emit entryEffectChanged(arg); + } +} + +void QQuickImageParticle::resetColor() +{ + m_explicitColor = false; + foreach (const QString &str, m_groups) + foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data) + if (d->colorOwner == this) + d->colorOwner = 0; + m_color = QColor(); + m_color_variation = 0.0f; + m_redVariation = 0.0f; + m_blueVariation = 0.0f; + m_greenVariation = 0.0f; + m_alpha = 1.0f; + m_alphaVariation = 0.0f; +} + +void QQuickImageParticle::resetRotation() +{ + m_explicitRotation = false; + foreach (const QString &str, m_groups) + foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data) + if (d->rotationOwner == this) + d->rotationOwner = 0; + m_rotation = 0; + m_rotationVariation = 0; + m_rotationSpeed = 0; + m_rotationSpeedVariation = 0; + m_autoRotation = false; +} + +void QQuickImageParticle::resetDeformation() +{ + m_explicitDeformation = false; + foreach (const QString &str, m_groups) + foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data) + if (d->deformationOwner == this) + d->deformationOwner = 0; + if (m_xVector) + delete m_xVector; + if (m_yVector) + delete m_yVector; + m_xVector = 0; + m_yVector = 0; +} + +void QQuickImageParticle::reset() +{ + QQuickParticlePainter::reset(); + m_pleaseReset = true; + update(); +} + +void QQuickImageParticle::createEngine() +{ + if (m_spriteEngine) + delete m_spriteEngine; + if (m_sprites.count()) { + m_spriteEngine = new QQuickSpriteEngine(m_sprites, this); + connect(m_spriteEngine, SIGNAL(stateChanged(int)), + this, SLOT(spriteAdvance(int)), Qt::DirectConnection); + m_explicitAnimation = true; + } else { + m_spriteEngine = 0; + m_explicitAnimation = false; + } + reset(); +} + +static QSGGeometry::Attribute SimpleParticle_Attributes[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position + QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data + QSGGeometry::Attribute::create(2, 4, GL_FLOAT) // Vectors +}; + +static QSGGeometry::AttributeSet SimpleParticle_AttributeSet = +{ + 3, // Attribute Count + ( 2 + 4 + 4 ) * sizeof(float), + SimpleParticle_Attributes +}; + +static QSGGeometry::Attribute ColoredParticle_Attributes[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position + QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data + QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors + QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors +}; + +static QSGGeometry::AttributeSet ColoredParticle_AttributeSet = +{ + 4, // Attribute Count + ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar), + ColoredParticle_Attributes +}; + +static QSGGeometry::Attribute DeformableParticle_Attributes[] = { + QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord + QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data + QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors + QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors + QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors + QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation +}; + +static QSGGeometry::AttributeSet DeformableParticle_AttributeSet = +{ + 6, // Attribute Count + (4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar), + DeformableParticle_Attributes +}; + +static QSGGeometry::Attribute SpriteParticle_Attributes[] = { + QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord + QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data + QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors + QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors + QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors + QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation + QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Anim Data + QSGGeometry::Attribute::create(7, 4, GL_FLOAT) // Anim Pos +}; + +static QSGGeometry::AttributeSet SpriteParticle_AttributeSet = +{ + 8, // Attribute Count + (4 + 4 + 4 + 4 + 3 + 3 + 4) * sizeof(float) + 4 * sizeof(uchar), + SpriteParticle_Attributes +}; + +void QQuickImageParticle::clearShadows() +{ + foreach (const QVector<QQuickParticleData*> data, m_shadowData) + qDeleteAll(data); + m_shadowData.clear(); +} + +//Only call if you need to, may initialize the whole array first time +QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum) +{ + //Will return datum if the datum is a sentinel or uninitialized, to centralize that one check + if (datum->systemIndex == -1) + return datum; + QQuickParticleGroupData* gd = m_system->groupData[datum->group]; + if (!m_shadowData.contains(datum->group)) { + QVector<QQuickParticleData*> data; + for (int i=0; i<gd->size(); i++){ + QQuickParticleData* datum = new QQuickParticleData(m_system); + *datum = *(gd->data[i]); + data << datum; + } + m_shadowData.insert(datum->group, data); + } + //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request + + return m_shadowData[datum->group][datum->index]; +} + +bool QQuickImageParticle::loadingSomething() +{ + return (m_image && m_image->pix.isLoading()) + || (m_colorTable && m_colorTable->pix.isLoading()) + || (m_sizeTable && m_sizeTable->pix.isLoading()) + || (m_opacityTable && m_opacityTable->pix.isLoading()) + || (m_spriteEngine && m_spriteEngine->isLoading()); +} + +void QQuickImageParticle::buildParticleNodes()//Starts async parts, like loading images. +{ + if (m_rootNode || loadingSomething()) + return; + + if (!m_buildingNodes) { + if (m_image) {//ImageData created on setSource + m_image->pix.clear(this); + m_image->pix.load(qmlEngine(this), m_image->source); + } + + if (m_spriteEngine) + m_spriteEngine->startAssemblingImage(); + + if (m_colorTable) + m_colorTable->pix.load(qmlEngine(this), m_colorTable->source); + + if (m_sizeTable) + m_sizeTable->pix.load(qmlEngine(this), m_sizeTable->source); + + if (m_opacityTable) + m_opacityTable->pix.load(qmlEngine(this), m_opacityTable->source); + + m_buildingNodes = true; + if (loadingSomething()) + return; + } + finishBuildParticleNodes(); +} + +void QQuickImageParticle::finishBuildParticleNodes() +{ + m_buildingNodes = false; +#ifdef QT_OPENGL_ES_2 + if (m_count * 4 > 0xffff) { + printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort + return; + } +#endif + + if (count() <= 0) + return; + + m_debugMode = m_system->m_debugMode; + + if (m_sprites.count() || m_bypassOptimizations) { + perfLevel = Sprites; + } else if (m_colorTable || m_sizeTable || m_opacityTable) { + perfLevel = Tabled; + } else if (m_autoRotation || m_rotation || m_rotationVariation + || m_rotationSpeed || m_rotationSpeedVariation + || m_xVector || m_yVector) { + perfLevel = Deformable; + } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation + || m_redVariation || m_blueVariation || m_greenVariation) { + perfLevel = Colored; + } else { + perfLevel = Simple; + } + + foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders + int gIdx = m_system->groupIds[str]; + foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){ + QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p); + if (other){ + if (other->perfLevel > perfLevel) { + if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite) + if (perfLevel < Deformable) + perfLevel = Deformable; + } else { + perfLevel = other->perfLevel; + } + } else if (other->perfLevel < perfLevel) { + other->reset(); + } + } + } + } +#ifdef Q_OS_WIN + if (perfLevel < Deformable) //QTBUG-24540 , point sprite 'extension' isn't working on windows. + perfLevel = Deformable; +#endif + + if (perfLevel >= Colored && !m_color.isValid()) + m_color = QColor(Qt::white);//Hidden default, but different from unset + + clearShadows(); + if (m_material) + m_material = 0; + + //Setup material + QImage colortable; + QImage sizetable; + QImage opacitytable; + QImage image; + bool imageLoaded = false; + switch (perfLevel) {//Fallthrough intended + case Sprites: + if (!m_spriteEngine) { + qWarning() << "ImageParticle: No sprite engine..."; + //Sprite performance mode with static image is supported, but not advised + //Note that in this case it always uses shadow data + } else { + image = m_spriteEngine->assembledImage(); + if (image.isNull())//Warning is printed in engine + return; + imageLoaded = true; + } + m_material = SpriteMaterial::createMaterial(); + if (imageLoaded) + getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image); + getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size()); + if (m_spriteEngine) + m_spriteEngine->setCount(m_count); + case Tabled: + if (!m_material) + m_material = TabledMaterial::createMaterial(); + + if (m_colorTable) { + if (m_colorTable->pix.isReady()) + colortable = m_colorTable->pix.image(); + else + qmlInfo(this) << "Error loading color table: " << m_colorTable->pix.error(); + } + + if (m_sizeTable) { + if (m_sizeTable->pix.isReady()) + sizetable = m_sizeTable->pix.image(); + else + qmlInfo(this) << "Error loading size table: " << m_sizeTable->pix.error(); + } + + if (m_opacityTable) { + if (m_opacityTable->pix.isReady()) + opacitytable = m_opacityTable->pix.image(); + else + qmlInfo(this) << "Error loading opacity table: " << m_opacityTable->pix.error(); + } + + if (colortable.isNull()){//###Goes through image just for this + colortable = QImage(1,1,QImage::Format_ARGB32_Premultiplied); + colortable.fill(Qt::white); + } + getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable); + fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE); + fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE); + case Deformable: + if (!m_material) + m_material = DeformableMaterial::createMaterial(); + case Colored: + if (!m_material) + m_material = ColoredMaterial::createMaterial(); + default://Also Simple + if (!m_material) + m_material = SimpleMaterial::createMaterial(); + if (!imageLoaded) { + if (!m_image->pix.isReady()) { + qmlInfo(this) << m_image->pix.error(); + delete m_material; + return; + } + //getState<ImageMaterialData>(m_material)->texture //TODO: Shouldn't this be better? But not crash? + // = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory()); + getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image()); + } + getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear); + getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect; + m_material->setFlag(QSGMaterial::Blending); + } + + m_nodes.clear(); + foreach (const QString &str, m_groups){ + int gIdx = m_system->groupIds[str]; + int count = m_system->groupData[gIdx]->size(); + QSGGeometryNode* node = new QSGGeometryNode(); + node->setMaterial(m_material); + node->markDirty(QSGNode::DirtyMaterial); + + m_nodes.insert(gIdx, node); + m_idxStarts.insert(gIdx, m_lastIdxStart); + m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx)); + m_lastIdxStart += count; + + //Create Particle Geometry + int vCount = count * 4; + int iCount = count * 6; + + QSGGeometry *g; + if (perfLevel == Sprites) + g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount); + else if (perfLevel == Tabled) + g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount); + else if (perfLevel == Deformable) + g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount); + else if (perfLevel == Colored) + g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0); + else //Simple + g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0); + + node->setGeometry(g); + if (perfLevel <= Colored){ + g->setDrawingMode(GL_POINTS); + if (m_debugMode){ + GLfloat pointSizeRange[2]; + glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange); + qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1]; + } + }else + g->setDrawingMode(GL_TRIANGLES); + + for (int p=0; p < count; ++p) + commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch + + if (perfLevel == Sprites) + initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount); + else if (perfLevel == Tabled) + initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount); + else if (perfLevel == Deformable) + initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount); + + if (perfLevel > Colored){ + quint16 *indices = g->indexDataAsUShort(); + for (int i=0; i < count; ++i) { + int o = i * 4; + indices[0] = o; + indices[1] = o + 1; + indices[2] = o + 2; + indices[3] = o + 1; + indices[4] = o + 3; + indices[5] = o + 2; + indices += 6; + } + } + } + + if (perfLevel == Sprites) + spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame + + foreach (QSGGeometryNode* node, m_nodes){ + if (node == *(m_nodes.begin())) + node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes + else + (*(m_nodes.begin()))->appendChildNode(node); + } + + m_rootNode = *(m_nodes.begin()); + update(); +} + +QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *) +{ + if (m_pleaseReset){ + m_lastLevel = perfLevel; + + delete m_rootNode;//Automatically deletes children, and SG manages material lifetime + m_rootNode = 0; + m_nodes.clear(); + + m_idxStarts.clear(); + m_startsIdx.clear(); + m_lastIdxStart = 0; + + m_material = 0; + + m_pleaseReset = false; + m_buildingNodes = false;//Cancel a part-way build + } + + if (m_system && m_system->isRunning() && !m_system->isPaused()){ + prepareNextFrame(); + if (m_rootNode) { + update(); + foreach (QSGGeometryNode* node, m_nodes) + node->markDirty(QSGNode::DirtyGeometry); + } else if (m_buildingNodes) { + update();//To call prepareNextFrame() again from the renderThread + } + } + + return m_rootNode; +} + +void QQuickImageParticle::prepareNextFrame() +{ + if (m_rootNode == 0){//TODO: Staggered loading (as emitted) + buildParticleNodes(); + if (m_debugMode) { + qDebug() << "QQuickImageParticle Feature level: " << perfLevel; + qDebug() << "QQuickImageParticle Nodes: "; + int count = 0; + foreach (int i, m_nodes.keys()) { + qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)"; + count += m_system->groupData[i]->size(); + } + qDebug() << "Total count: " << count; + } + if (m_rootNode == 0) + return; + } + qint64 timeStamp = m_system->systemSync(this); + + qreal time = timeStamp / 1000.; + + switch (perfLevel){//Fall-through intended + case Sprites: + //Advance State + if (m_spriteEngine) + m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed + spritesUpdate(time); + case Tabled: + case Deformable: + case Colored: + case Simple: + default: //Also Simple + getState<ImageMaterialData>(m_material)->timestamp = time; + break; + } + foreach (QSGGeometryNode* node, m_nodes) + node->markDirty(QSGNode::DirtyMaterial); +} + +void QQuickImageParticle::spritesUpdate(qreal time) +{ + // Sprite progression handled CPU side, so as to have per-frame control. + foreach (const QString &str, m_groups) { + int gIdx = m_system->groupIds[str]; + foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) { + QSGGeometryNode *node = m_nodes[gIdx]; + if (!node) + continue; + //TODO: Interpolate between two different animations if it's going to transition next frame + // This is particularly important for cut-up sprites. + QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum)); + int spriteIdx = 0; + for (int i = 0; i<m_startsIdx.count(); i++) { + if (m_startsIdx[i].second == gIdx){ + spriteIdx = m_startsIdx[i].first + datum->index; + break; + } + } + + double frameAt; + qreal progress = 0; + + if (datum->frameDuration > 0) { + qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0); + frame = qBound((qreal)0.0, frame, (qreal)((qreal)datum->frameCount - 1.0));//Stop at count-1 frames until we have between anim interpolation + if (m_spritesInterpolate) + progress = modf(frame,&frameAt); + else + modf(frame,&frameAt); + } else { + datum->frameAt++; + if (datum->frameAt >= datum->frameCount){ + datum->frameAt = 0; + m_spriteEngine->advance(spriteIdx); + } + frameAt = datum->frameAt; + } + if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too? + frameAt = (datum->frameCount - 1) - frameAt; + QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize; + qreal y = datum->animY / sheetSize.height(); + qreal w = datum->animWidth / sheetSize.width(); + qreal h = datum->animHeight / sheetSize.height(); + qreal x1 = datum->animX / sheetSize.width(); + x1 += frameAt * w; + qreal x2 = x1; + if (frameAt < (datum->frameCount-1)) + x2 += w; + + node->setFlag(QSGNode::OwnsGeometry, false); + SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData(); + spriteVertices += datum->index*4; + for (int i=0; i<4; i++) { + spriteVertices[i].animX1 = x1; + spriteVertices[i].animY1 = y; + spriteVertices[i].animX2 = x2; + spriteVertices[i].animY2 = y; + spriteVertices[i].animW = w; + spriteVertices[i].animH = h; + spriteVertices[i].animProgress = progress; + } + node->setFlag(QSGNode::OwnsGeometry, true); + } + } +} + +void QQuickImageParticle::spriteAdvance(int spriteIdx) +{ + if (!m_startsIdx.count())//Probably overly defensive + return; + + int gIdx = -1; + int i; + for (i = 0; i<m_startsIdx.count(); i++) { + if (spriteIdx < m_startsIdx[i].first) { + gIdx = m_startsIdx[i-1].second; + break; + } + } + if (gIdx == -1) + gIdx = m_startsIdx[i-1].second; + int pIdx = spriteIdx - m_startsIdx[i-1].first; + + QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx]; + QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum)); + + datum->animIdx = m_spriteEngine->spriteState(spriteIdx); + datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0; + datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx); + datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / datum->frameCount; + datum->animX = m_spriteEngine->spriteX(spriteIdx); + datum->animY = m_spriteEngine->spriteY(spriteIdx); + datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx); + datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx); +} + +void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d) +{ + d->color = c; + //TODO: get index for reload - or make function take an index +} + +void QQuickImageParticle::initialize(int gIdx, int pIdx) +{ + Color4ub color; + QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx]; + qreal redVariation = m_color_variation + m_redVariation; + qreal greenVariation = m_color_variation + m_greenVariation; + qreal blueVariation = m_color_variation + m_blueVariation; + int spriteIdx = 0; + if (m_spriteEngine) { + spriteIdx = m_idxStarts[gIdx] + datum->index; + if (spriteIdx >= m_spriteEngine->count()) + m_spriteEngine->setCount(spriteIdx+1); + } + + float rotation; + float rotationSpeed; + float autoRotate; + switch (perfLevel){//Fall-through is intended on all of them + case Sprites: + // Initial Sprite State + if (m_explicitAnimation && m_spriteEngine){ + if (!datum->animationOwner) + datum->animationOwner = this; + QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum)); + writeTo->animT = writeTo->t; + //writeTo->animInterpolate = m_spritesInterpolate; + if (m_spriteEngine){ + m_spriteEngine->start(spriteIdx); + writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx); + writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / writeTo->frameCount; + writeTo->animIdx = 0;//Always starts at 0 + writeTo->frameAt = -1; + writeTo->animX = m_spriteEngine->spriteX(spriteIdx); + writeTo->animY = m_spriteEngine->spriteY(spriteIdx); + writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx); + writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx); + } + } else { + QQuickParticleData* writeTo = getShadowDatum(datum); + writeTo->animT = datum->t; + writeTo->frameCount = 1; + writeTo->frameDuration = 60000000.0; + writeTo->frameAt = -1; + writeTo->animIdx = 0; + writeTo->animT = 0; + writeTo->animX = writeTo->animY = 0; + writeTo->animWidth = getState<ImageMaterialData>(m_material)->animSheetSize.width(); + writeTo->animHeight = getState<ImageMaterialData>(m_material)->animSheetSize.height(); + } + case Tabled: + case Deformable: + //Initial Rotation + if (m_explicitDeformation){ + if (!datum->deformationOwner) + datum->deformationOwner = this; + if (m_xVector){ + const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y)); + if (datum->deformationOwner == this) { + datum->xx = ret.x(); + datum->xy = ret.y(); + } else { + getShadowDatum(datum)->xx = ret.x(); + getShadowDatum(datum)->xy = ret.y(); + } + } + if (m_yVector){ + const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y)); + if (datum->deformationOwner == this) { + datum->yx = ret.x(); + datum->yy = ret.y(); + } else { + getShadowDatum(datum)->yx = ret.x(); + getShadowDatum(datum)->yy = ret.y(); + } + } + } + + if (m_explicitRotation){ + if (!datum->rotationOwner) + datum->rotationOwner = this; + rotation = + (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV; + rotationSpeed = + (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV; + autoRotate = m_autoRotation?1.0:0.0; + if (datum->rotationOwner == this) { + datum->rotation = rotation; + datum->rotationSpeed = rotationSpeed; + datum->autoRotate = autoRotate; + } else { + getShadowDatum(datum)->rotation = rotation; + getShadowDatum(datum)->rotationSpeed = rotationSpeed; + getShadowDatum(datum)->autoRotate = autoRotate; + } + } + case Colored: + //Color initialization + // Particle color + if (m_explicitColor) { + if (!datum->colorOwner) + datum->colorOwner = this; + color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation; + color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation; + color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation; + color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation; + if (datum->colorOwner == this) + datum->color = color; + else + getShadowDatum(datum)->color = color; + } + default: + break; + } +} + +void QQuickImageParticle::commit(int gIdx, int pIdx) +{ + if (m_pleaseReset) + return; + QSGGeometryNode *node = m_nodes[gIdx]; + if (!node) + return; + QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx]; + node->setFlag(QSGNode::OwnsGeometry, false); + SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData(); + DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData(); + ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData(); + SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData(); + switch (perfLevel){//No automatic fall through intended on this one + case Sprites: + spriteVertices += pIdx*4; + for (int i=0; i<4; i++){ + spriteVertices[i].x = datum->x - m_systemOffset.x(); + spriteVertices[i].y = datum->y - m_systemOffset.y(); + spriteVertices[i].t = datum->t; + spriteVertices[i].lifeSpan = datum->lifeSpan; + spriteVertices[i].size = datum->size; + spriteVertices[i].endSize = datum->endSize; + spriteVertices[i].vx = datum->vx; + spriteVertices[i].vy = datum->vy; + spriteVertices[i].ax = datum->ax; + spriteVertices[i].ay = datum->ay; + if (m_explicitDeformation && datum->deformationOwner != this) { + QQuickParticleData* shadow = getShadowDatum(datum); + spriteVertices[i].xx = shadow->xx; + spriteVertices[i].xy = shadow->xy; + spriteVertices[i].yx = shadow->yx; + spriteVertices[i].yy = shadow->yy; + } else { + spriteVertices[i].xx = datum->xx; + spriteVertices[i].xy = datum->xy; + spriteVertices[i].yx = datum->yx; + spriteVertices[i].yy = datum->yy; + } + if (m_explicitRotation && datum->rotationOwner != this) { + QQuickParticleData* shadow = getShadowDatum(datum); + spriteVertices[i].rotation = shadow->rotation; + spriteVertices[i].rotationSpeed = shadow->rotationSpeed; + spriteVertices[i].autoRotate = shadow->autoRotate; + } else { + spriteVertices[i].rotation = datum->rotation; + spriteVertices[i].rotationSpeed = datum->rotationSpeed; + spriteVertices[i].autoRotate = datum->autoRotate; + } + //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand + if (m_explicitColor && datum->colorOwner != this) { + QQuickParticleData* shadow = getShadowDatum(datum); + spriteVertices[i].color.r = shadow->color.r; + spriteVertices[i].color.g = shadow->color.g; + spriteVertices[i].color.b = shadow->color.b; + spriteVertices[i].color.a = shadow->color.a; + } else { + spriteVertices[i].color.r = datum->color.r; + spriteVertices[i].color.g = datum->color.g; + spriteVertices[i].color.b = datum->color.b; + spriteVertices[i].color.a = datum->color.a; + } + } + break; + case Tabled: //Fall through until it has its own vertex class + case Deformable: + deformableVertices += pIdx*4; + for (int i=0; i<4; i++){ + deformableVertices[i].x = datum->x - m_systemOffset.x(); + deformableVertices[i].y = datum->y - m_systemOffset.y(); + deformableVertices[i].t = datum->t; + deformableVertices[i].lifeSpan = datum->lifeSpan; + deformableVertices[i].size = datum->size; + deformableVertices[i].endSize = datum->endSize; + deformableVertices[i].vx = datum->vx; + deformableVertices[i].vy = datum->vy; + deformableVertices[i].ax = datum->ax; + deformableVertices[i].ay = datum->ay; + if (m_explicitDeformation && datum->deformationOwner != this) { + QQuickParticleData* shadow = getShadowDatum(datum); + deformableVertices[i].xx = shadow->xx; + deformableVertices[i].xy = shadow->xy; + deformableVertices[i].yx = shadow->yx; + deformableVertices[i].yy = shadow->yy; + } else { + deformableVertices[i].xx = datum->xx; + deformableVertices[i].xy = datum->xy; + deformableVertices[i].yx = datum->yx; + deformableVertices[i].yy = datum->yy; + } + if (m_explicitRotation && datum->rotationOwner != this) { + QQuickParticleData* shadow = getShadowDatum(datum); + deformableVertices[i].rotation = shadow->rotation; + deformableVertices[i].rotationSpeed = shadow->rotationSpeed; + deformableVertices[i].autoRotate = shadow->autoRotate; + } else { + deformableVertices[i].rotation = datum->rotation; + deformableVertices[i].rotationSpeed = datum->rotationSpeed; + deformableVertices[i].autoRotate = datum->autoRotate; + } + if (m_explicitColor && datum->colorOwner != this) { + QQuickParticleData* shadow = getShadowDatum(datum); + deformableVertices[i].color.r = shadow->color.r; + deformableVertices[i].color.g = shadow->color.g; + deformableVertices[i].color.b = shadow->color.b; + deformableVertices[i].color.a = shadow->color.a; + } else { + deformableVertices[i].color.r = datum->color.r; + deformableVertices[i].color.g = datum->color.g; + deformableVertices[i].color.b = datum->color.b; + deformableVertices[i].color.a = datum->color.a; + } + } + break; + case Colored: + coloredVertices += pIdx*1; + for (int i=0; i<1; i++){ + coloredVertices[i].x = datum->x - m_systemOffset.x(); + coloredVertices[i].y = datum->y - m_systemOffset.y(); + coloredVertices[i].t = datum->t; + coloredVertices[i].lifeSpan = datum->lifeSpan; + coloredVertices[i].size = datum->size; + coloredVertices[i].endSize = datum->endSize; + coloredVertices[i].vx = datum->vx; + coloredVertices[i].vy = datum->vy; + coloredVertices[i].ax = datum->ax; + coloredVertices[i].ay = datum->ay; + if (m_explicitColor && datum->colorOwner != this) { + QQuickParticleData* shadow = getShadowDatum(datum); + coloredVertices[i].color.r = shadow->color.r; + coloredVertices[i].color.g = shadow->color.g; + coloredVertices[i].color.b = shadow->color.b; + coloredVertices[i].color.a = shadow->color.a; + } else { + coloredVertices[i].color.r = datum->color.r; + coloredVertices[i].color.g = datum->color.g; + coloredVertices[i].color.b = datum->color.b; + coloredVertices[i].color.a = datum->color.a; + } + } + break; + case Simple: + simpleVertices += pIdx*1; + for (int i=0; i<1; i++){ + simpleVertices[i].x = datum->x - m_systemOffset.x(); + simpleVertices[i].y = datum->y - m_systemOffset.y(); + simpleVertices[i].t = datum->t; + simpleVertices[i].lifeSpan = datum->lifeSpan; + simpleVertices[i].size = datum->size; + simpleVertices[i].endSize = datum->endSize; + simpleVertices[i].vx = datum->vx; + simpleVertices[i].vy = datum->vy; + simpleVertices[i].ax = datum->ax; + simpleVertices[i].ay = datum->ay; + } + break; + default: + break; + } + + node->setFlag(QSGNode::OwnsGeometry, true); +} + + + +QT_END_NAMESPACE diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h new file mode 100644 index 0000000000..4db2c9801a --- /dev/null +++ b/src/particles/qquickimageparticle_p.h @@ -0,0 +1,446 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 ULTRAPARTICLE_H +#define ULTRAPARTICLE_H +#include "qquickparticlepainter_p.h" +#include "qquickdirection_p.h" +#include <private/qquickpixmapcache_p.h> +#include <QQmlListProperty> +#include <QtQuick/qsgsimplematerial.h> +#include <QtGui/qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ImageMaterialData; +class QSGGeometryNode; + +class QQuickSprite; +class QQuickStochasticEngine; + +struct SimpleVertex { + float x; + float y; + float t; + float lifeSpan; + float size; + float endSize; + float vx; + float vy; + float ax; + float ay; +}; + +struct ColoredVertex { + float x; + float y; + float t; + float lifeSpan; + float size; + float endSize; + float vx; + float vy; + float ax; + float ay; + Color4ub color; +}; + +struct DeformableVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float vx; + float vy; + float ax; + float ay; + Color4ub color; + float xx; + float xy; + float yx; + float yy; + float rotation; + float rotationSpeed; + float autoRotate;//Assumed that GPUs prefer floats to bools +}; + +struct SpriteVertex { + float x; + float y; + float tx; + float ty; + float t; + float lifeSpan; + float size; + float endSize; + float vx; + float vy; + float ax; + float ay; + Color4ub color; + float xx; + float xy; + float yx; + float yy; + float rotation; + float rotationSpeed; + float autoRotate;//Assumed that GPUs prefer floats to bools + float animW; + float animH; + float animProgress; + float animX1; + float animY1; + float animX2; + float animY2; +}; + +template <typename Vertex> +struct Vertices { + Vertex v1; + Vertex v2; + Vertex v3; + Vertex v4; +}; + +class QQuickImageParticle : public QQuickParticlePainter +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ image WRITE setImage NOTIFY imageChanged) + Q_PROPERTY(QQmlListProperty<QQuickSprite> sprites READ sprites) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + //### Is it worth having progress like Image has? + //Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + + Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged) + Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged) + Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged) + + //###Now just colorize - add a flag for 'solid' color particles(where the img is just a mask?)? + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged RESET resetColor) + //Stacks (added) with individual colorVariations + Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged RESET resetColor) + Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged RESET resetColor) + Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged RESET resetColor) + Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged RESET resetColor) + //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha) + Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged RESET resetColor) + Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged RESET resetColor) + + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged RESET resetRotation) + Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged RESET resetRotation) + Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged RESET resetRotation) + Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged RESET resetRotation) + //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation + //to 180 will lead to facing away from the direction of motion + Q_PROPERTY(bool autoRotation READ autoRotation WRITE setAutoRotation NOTIFY autoRotationChanged RESET resetRotation) + + //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size + Q_PROPERTY(QQuickDirection* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged RESET resetDeformation) + //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram. + Q_PROPERTY(QQuickDirection* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged RESET resetDeformation) + Q_PROPERTY(bool spritesInterpolate READ spritesInterpolate WRITE setSpritesInterpolate NOTIFY spritesInterpolateChanged) + + Q_PROPERTY(EntryEffect entryEffect READ entryEffect WRITE setEntryEffect NOTIFY entryEffectChanged) + Q_ENUMS(EntryEffect) + Q_ENUMS(Status) +public: + explicit QQuickImageParticle(QQuickItem *parent = 0); + virtual ~QQuickImageParticle(); + + enum Status { Null, Ready, Loading, Error }; + + QQmlListProperty<QQuickSprite> sprites(); + QQuickStochasticEngine* spriteEngine() {return m_spriteEngine;} + + enum EntryEffect { + None = 0, + Fade = 1, + Scale = 2 + }; + + enum PerformanceLevel{//TODO: Expose? + Unknown = 0, + Simple, + Colored, + Deformable, + Tabled, + Sprites + }; + + QUrl image() const { return m_image ? m_image->source : QUrl(); } + void setImage(const QUrl &image); + + QUrl colortable() const { return m_colorTable ? m_colorTable->source : QUrl(); } + void setColortable(const QUrl &table); + + QUrl sizetable() const { return m_sizeTable ? m_sizeTable->source : QUrl(); } + void setSizetable (const QUrl &table); + + QUrl opacitytable() const { return m_opacityTable ? m_opacityTable->source : QUrl(); } + void setOpacitytable(const QUrl &table); + + QColor color() const { return m_color; } + void setColor(const QColor &color); + + qreal colorVariation() const { return m_color_variation; } + void setColorVariation(qreal var); + + qreal alphaVariation() const { return m_alphaVariation; } + + qreal alpha() const { return m_alpha; } + + qreal redVariation() const { return m_redVariation; } + + qreal greenVariation() const { return m_greenVariation; } + + qreal blueVariation() const { return m_blueVariation; } + + qreal rotation() const { return m_rotation; } + + qreal rotationVariation() const { return m_rotationVariation; } + + qreal rotationSpeed() const { return m_rotationSpeed; } + + qreal rotationSpeedVariation() const { return m_rotationSpeedVariation; } + + bool autoRotation() const { return m_autoRotation; } + + QQuickDirection* xVector() const { return m_xVector; } + + QQuickDirection* yVector() const { return m_yVector; } + + bool spritesInterpolate() const { return m_spritesInterpolate; } + + bool bypassOptimizations() const { return m_bypassOptimizations; } + + EntryEffect entryEffect() const { return m_entryEffect; } + + Status status() const { return m_status; } + + void resetColor(); + void resetRotation(); + void resetDeformation(); + +signals: + + void imageChanged(); + void colortableChanged(); + void sizetableChanged(); + void opacitytableChanged(); + + void colorChanged(); + void colorVariationChanged(); + + void alphaVariationChanged(qreal arg); + + void alphaChanged(qreal arg); + + void redVariationChanged(qreal arg); + + void greenVariationChanged(qreal arg); + + void blueVariationChanged(qreal arg); + + void rotationChanged(qreal arg); + + void rotationVariationChanged(qreal arg); + + void rotationSpeedChanged(qreal arg); + + void rotationSpeedVariationChanged(qreal arg); + + void autoRotationChanged(bool arg); + + void xVectorChanged(QQuickDirection* arg); + + void yVectorChanged(QQuickDirection* arg); + + void spritesInterpolateChanged(bool arg); + + void bypassOptimizationsChanged(bool arg); + + void entryEffectChanged(EntryEffect arg); + + void statusChanged(Status arg); + +public slots: + void reloadColor(const Color4ub &c, QQuickParticleData* d); + void setAlphaVariation(qreal arg); + + void setAlpha(qreal arg); + + void setRedVariation(qreal arg); + + void setGreenVariation(qreal arg); + + void setBlueVariation(qreal arg); + + void setRotation(qreal arg); + + void setRotationVariation(qreal arg); + + void setRotationSpeed(qreal arg); + + void setRotationSpeedVariation(qreal arg); + + void setAutoRotation(bool arg); + + void setXVector(QQuickDirection* arg); + + void setYVector(QQuickDirection* arg); + + void setSpritesInterpolate(bool arg); + + void setBypassOptimizations(bool arg); + + void setEntryEffect(EntryEffect arg); + +protected: + void reset(); + virtual void initialize(int gIdx, int pIdx); + virtual void commit(int gIdx, int pIdx); + + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + void prepareNextFrame(); + void buildParticleNodes(); + + void sceneGraphInvalidated(); + +private slots: + void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty + + void spriteAdvance(int spriteIndex); + void spritesUpdate(qreal time = 0 ); + void finishBuildParticleNodes(); +private: + struct ImageData { + QUrl source; + QQuickPixmap pix; + }; + ImageData *m_image; + ImageData *m_colorTable; + ImageData *m_sizeTable; + ImageData *m_opacityTable; + bool loadingSomething(); + + + QColor m_color; + qreal m_color_variation; + + QSGGeometryNode *m_rootNode; + QHash<int, QSGGeometryNode *> m_nodes; + QHash<int, int> m_idxStarts;//TODO: Proper resizing will lead to needing a spriteEngine per particle - do this after sprite engine gains transparent sharing? + QList<QPair<int, int> > m_startsIdx;//Same data, optimized for alternate retrieval + + int m_lastIdxStart; + QSGMaterial *m_material; + + // derived values... + + qreal m_alphaVariation; + qreal m_alpha; + qreal m_redVariation; + qreal m_greenVariation; + qreal m_blueVariation; + qreal m_rotation; + qreal m_rotationVariation; + qreal m_rotationSpeed; + qreal m_rotationSpeedVariation; + bool m_autoRotation; + QQuickDirection* m_xVector; + QQuickDirection* m_yVector; + + QList<QQuickSprite*> m_sprites; + QQuickSpriteEngine* m_spriteEngine; + bool m_spritesInterpolate; + + bool m_explicitColor; + bool m_explicitRotation; + bool m_explicitDeformation; + bool m_explicitAnimation; + QHash<int, QVector<QQuickParticleData*> > m_shadowData; + void clearShadows(); + QQuickParticleData* getShadowDatum(QQuickParticleData* datum); + + bool m_bypassOptimizations; + PerformanceLevel perfLevel; + + PerformanceLevel m_lastLevel; + bool m_debugMode; + + template<class Vertex> + void initTexCoords(Vertex* v, int count){ + Vertex* end = v + count; + while (v < end){ + v[0].tx = 0; + v[0].ty = 0; + + v[1].tx = 1; + v[1].ty = 0; + + v[2].tx = 0; + v[2].ty = 1; + + v[3].tx = 1; + v[3].ty = 1; + + v += 4; + } + } + + template<class MaterialData> + MaterialData* getState(QSGMaterial* m){ + return static_cast<QSGSimpleMaterial<MaterialData> *>(m)->state(); + } + EntryEffect m_entryEffect; + Status m_status; + bool m_buildingNodes; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ULTRAPARTICLE_H diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp new file mode 100644 index 0000000000..91ef06fcb2 --- /dev/null +++ b/src/particles/qquickitemparticle.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickitemparticle_p.h" +#include <private/qquickvisualitemmodel_p.h> +#include <QtQuick/qsgnode.h> +#include <QTimer> +#include <QQmlComponent> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass ItemParticle QQuickItemParticle + \inqmlmodule QtQuick.Particles 2 + \inherits ParticlePainter + \brief The ItemParticle element allows you to specify your own delegate to paint particles. + +*/ + + +/*! + \qmlmethod void QtQuick.Particles2::ItemParticle::freeze(Item item) + + Suspends the flow of time for the logical particle which item represents, allowing you to control its movement. +*/ + +/*! + \qmlmethod void QtQuick.Particles2::ItemParticle::unfreeze(Item item) + + Restarts the flow of time for the logical particle which item represents, allowing it to be moved by the particle system again. +*/ + +/*! + \qmlmethod void QtQuick.Particles2::ItemParticle::take(Item item, bool prioritize) + + Asks the ItemParticle to take over control of item. It will be emitted when there is a logical particle available. + + By default items form a queue when waiting for a logical particle, but if prioritize is true then it will go immediately to the + head of the queue. +*/ +/*! + \qmlmethod void QtQuick.Particles2::ItemParticle::give(Item item) + + Orders the ItemParticle to give you control of the item. It will cease controlling it and the item will lose its association to the logical particle. +*/ + +/*! + \qmlproperty bool QtQuick.Particles2::ItemParticle::fade + + If true, the item will automatically be faded in and out + at the ends of its lifetime. If false, you will have to + implement any entry effect yourself. + + Default is true. +*/ +/*! + \qmlproperty Component QtQuick.Particles2::ItemParticle::delegate + + An instance of the delegate will be created for every logical + particle, and moved along with it. +*/ + +QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) : + QQuickParticlePainter(parent), m_fade(true), m_delegate(0) +{ + setFlag(QQuickItem::ItemHasContents); + clock = new Clock(this); + clock->start(); +} + +QQuickItemParticle::~QQuickItemParticle() +{ + delete clock; +} + +void QQuickItemParticle::freeze(QQuickItem* item) +{ + m_stasis << item; +} + + +void QQuickItemParticle::unfreeze(QQuickItem* item) +{ + m_stasis.remove(item); +} + +void QQuickItemParticle::take(QQuickItem *item, bool prioritize) +{ + if (prioritize) + m_pendingItems.push_front(item); + else + m_pendingItems.push_back(item); +} + +void QQuickItemParticle::give(QQuickItem *item) +{ + //TODO: This + Q_UNUSED(item); +} + +void QQuickItemParticle::initialize(int gIdx, int pIdx) +{ + m_loadables << m_system->groupData[gIdx]->data[pIdx];//defer to other thread +} + +void QQuickItemParticle::commit(int, int) +{ +} + +void QQuickItemParticle::tick(int time) +{ + Q_UNUSED(time);//only needed because QTickAnimationProxy expects one + foreach (QQuickItem* item, m_deletables){ + if (m_fade) + item->setOpacity(0.); + item->setVisible(false); + QQuickItemParticleAttached* mpa; + if ((mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(item)))) + mpa->detach();//reparent as well? + //TODO: Delete iff we created it + m_activeCount--; + } + m_deletables.clear(); + + foreach (QQuickParticleData* d, m_loadables){ + if (m_stasis.contains(d->delegate)) + qWarning() << "Current model particles prefers overwrite:false"; + //remove old item from the particle that is dying to make room for this one + if (d->delegate) + m_deletables << d->delegate; + d->delegate = 0; + if (!m_pendingItems.isEmpty()){ + d->delegate = m_pendingItems.front(); + m_pendingItems.pop_front(); + }else if (m_delegate){ + d->delegate = qobject_cast<QQuickItem*>(m_delegate->create(qmlContext(this))); + } + if (d->delegate && d){//###Data can be zero if creating an item leads to a reset - this screws things up. + d->delegate->setX(d->curX() - d->delegate->width()/2);//TODO: adjust for system? + d->delegate->setY(d->curY() - d->delegate->height()/2); + QQuickItemParticleAttached* mpa = qobject_cast<QQuickItemParticleAttached*>(qmlAttachedPropertiesObject<QQuickItemParticle>(d->delegate)); + if (mpa){ + mpa->m_mp = this; + mpa->attach(); + } + d->delegate->setParentItem(this); + if (m_fade) + d->delegate->setOpacity(0.); + d->delegate->setVisible(false);//Will be set to true when we prepare the next frame + m_activeCount++; + } + } + m_loadables.clear(); +} + +void QQuickItemParticle::reset() +{ + QQuickParticlePainter::reset(); + m_loadables.clear(); + //TODO: Cleanup items? + //deletables? +} + + +QSGNode* QQuickItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) +{ + //Dummy update just to get painting tick + if (m_pleaseReset){ + m_pleaseReset = false; + //Refill loadables, delayed here so as to only happen once per frame max + //### Constant resetting might lead to m_loadables never being populated when tick() occurs + foreach (const QString group, m_groups){ + int gIdx = m_system->groupIds[group]; + foreach (QQuickParticleData* d, m_system->groupData[gIdx]->data) + if (!d->delegate && d->t != -1 && d->stillAlive()) + m_loadables << d; + } + } + prepareNextFrame(); + + update();//Get called again + if (n) + n->markDirty(QSGNode::DirtyMaterial); + return QQuickItem::updatePaintNode(n,d); +} + +void QQuickItemParticle::prepareNextFrame() +{ + if (!m_system) + return; + qint64 timeStamp = m_system->systemSync(this); + qreal curT = timeStamp/1000.0; + qreal dt = curT - m_lastT; + m_lastT = curT; + if (!m_activeCount) + return; + + //TODO: Size, better fade? + foreach (const QString &str, m_groups){ + int gIdx = m_system->groupIds[str]; + int count = m_system->groupData[gIdx]->size(); + + for (int i=0; i<count; i++){ + QQuickParticleData* data = m_system->groupData[gIdx]->data[i]; + QQuickItem* item = data->delegate; + if (!item) + continue; + qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan; + if (m_stasis.contains(item)) { + data->t += dt;//Stasis effect + continue; + } + if (t >= 1.0){//Usually happens from load + m_deletables << item; + data->delegate = 0; + }else{//Fade + data->delegate->setVisible(true); + if (m_fade){ + qreal o = 1.; + if (t<0.2) + o = t*5; + if (t>0.8) + o = (1-t)*5; + item->setOpacity(o); + } + } + item->setX(data->curX() - item->width()/2 - m_systemOffset.x()); + item->setY(data->curY() - item->height()/2 - m_systemOffset.y()); + } + } +} + +QQuickItemParticleAttached *QQuickItemParticle::qmlAttachedProperties(QObject *object) +{ + return new QQuickItemParticleAttached(object); +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickitemparticle_p.h b/src/particles/qquickitemparticle_p.h new file mode 100644 index 0000000000..c7b8a2661e --- /dev/null +++ b/src/particles/qquickitemparticle_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 ITEMPARTICLE_H +#define ITEMPARTICLE_H +#include "qquickparticlepainter_p.h" +#include <QPointer> +#include <QSet> +#include <private/qquickanimation_p_p.h> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickVisualDataModel; +class QQuickItemParticleAttached; + +class QQuickItemParticle : public QQuickParticlePainter +{ + Q_OBJECT + Q_PROPERTY(bool fade READ fade WRITE setFade NOTIFY fadeChanged) + Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) +public: + explicit QQuickItemParticle(QQuickItem *parent = 0); + ~QQuickItemParticle(); + + bool fade() const { return m_fade; } + + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + + static QQuickItemParticleAttached *qmlAttachedProperties(QObject *object); + QQmlComponent* delegate() const + { + return m_delegate; + } + +signals: + void fadeChanged(); + + void delegateChanged(QQmlComponent* arg); + +public slots: + //TODO: Add a follow mode, where moving the delegate causes the logical particle to go with it? + void freeze(QQuickItem* item); + void unfreeze(QQuickItem* item); + void take(QQuickItem* item,bool prioritize=false);//take by modelparticle + void give(QQuickItem* item);//give from modelparticle + + void setFade(bool arg){if (arg == m_fade) return; m_fade = arg; emit fadeChanged();} + void setDelegate(QQmlComponent* arg) + { + if (m_delegate != arg) { + m_delegate = arg; + emit delegateChanged(arg); + } + } + +protected: + virtual void reset(); + virtual void commit(int gIdx, int pIdx); + virtual void initialize(int gIdx, int pIdx); + void prepareNextFrame(); +private: + void tick(int time = 0); + QList<QQuickItem* > m_deletables; + QList< QQuickParticleData* > m_loadables; + bool m_fade; + + QList<QQuickItem*> m_pendingItems; + QList<int> m_available; + QSet<QQuickItem*> m_stasis; + qreal m_lastT; + int m_activeCount; + QQmlComponent* m_delegate; + + typedef QTickAnimationProxy<QQuickItemParticle, &QQuickItemParticle::tick> Clock; + Clock *clock; +}; + +class QQuickItemParticleAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickItemParticle* particle READ particle CONSTANT); +public: + QQuickItemParticleAttached(QObject* parent) + : QObject(parent), m_mp(0) + {;} + QQuickItemParticle* particle() {return m_mp;} + void detach(){emit detached();} + void attach(){emit attached();} +private: + QQuickItemParticle* m_mp; + friend class QQuickItemParticle; +Q_SIGNALS: + void detached(); + void attached(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPEINFO(QQuickItemParticle, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER +#endif // ITEMPARTICLE_H diff --git a/src/particles/qquicklineextruder.cpp b/src/particles/qquicklineextruder.cpp new file mode 100644 index 0000000000..f555de3f72 --- /dev/null +++ b/src/particles/qquicklineextruder.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquicklineextruder_p.h" +#include <stdlib.h> +#include <cmath> + +/*! + \qmlclass LineShape QQuickLineExtruder + \inqmlmodule QtQuick.Particles 2 + \inherits Shape + \brief The LineShape represents a line to Affectors and Emitter + +*/ + +/*! + \qmlproperty bool QtQuick.Particles2::LineShape::mirrored + + By default, the line goes from (0,0) to (width, height) of the item that + this shape is being applied to. + + If mirrored is set to true, this will be mirrored along the y axis. + The line will then go from (0,height) to (width, 0). +*/ + +QQuickLineExtruder::QQuickLineExtruder(QObject *parent) : + QQuickParticleExtruder(parent), m_mirrored(false) +{ +} + +QPointF QQuickLineExtruder::extrude(const QRectF &r) +{ + qreal x,y; + if (!r.height()){ + x = r.width() * ((qreal)rand())/RAND_MAX; + y = 0; + }else{ + y = r.height() * ((qreal)rand())/RAND_MAX; + if (!r.width()){ + x = 0; + }else{ + x = r.width()/r.height() * y; + if (m_mirrored) + x = r.width() - x; + } + } + return QPointF(x,y); +} diff --git a/src/particles/qquicklineextruder_p.h b/src/particles/qquicklineextruder_p.h new file mode 100644 index 0000000000..6f2b2493a6 --- /dev/null +++ b/src/particles/qquicklineextruder_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 LINEEXTRUDER_H +#define LINEEXTRUDER_H +#include "qquickparticleextruder_p.h" + +class QQuickLineExtruder : public QQuickParticleExtruder +{ + Q_OBJECT + //Default is topleft to bottom right. Flipped makes it topright to bottom left + Q_PROPERTY(bool mirrored READ mirrored WRITE setmirrored NOTIFY mirroredChanged) + +public: + explicit QQuickLineExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + bool mirrored() const + { + return m_mirrored; + } + +signals: + + void mirroredChanged(bool arg); + +public slots: + + void setmirrored(bool arg) + { + if (m_mirrored != arg) { + m_mirrored = arg; + emit mirroredChanged(arg); + } + } +private: + bool m_mirrored; +}; + +#endif // LINEEXTRUDER_H diff --git a/src/particles/qquickmaskextruder.cpp b/src/particles/qquickmaskextruder.cpp new file mode 100644 index 0000000000..50b71749c0 --- /dev/null +++ b/src/particles/qquickmaskextruder.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickmaskextruder_p.h" +#include <QtQml/qqml.h> +#include <QtQml/qqmlinfo.h> +#include <QImage> +#include <QDebug> +QT_BEGIN_NAMESPACE +/*! + \qmlclass MaskShape QQuickMaskExtruder + \inqmlmodule QtQuick.Particles 2 + \inherits Shape + \brief The MaskShape element allows you to represent an image as a shape to affectors and emitters. + +*/ +/*! + \qmlproperty url QtQuick.Particles2::MaskShape::source + + The image to use as the mask. Areas with non-zero opacity + will be considered inside the shape. +*/ + + +QQuickMaskExtruder::QQuickMaskExtruder(QObject *parent) : + QQuickParticleExtruder(parent) + , m_lastWidth(-1) + , m_lastHeight(-1) +{ +} + +void QQuickMaskExtruder::setSource(QUrl arg) +{ + if (m_source != arg) { + m_source = arg; + + m_lastHeight = -1;//Trigger reset + m_lastWidth = -1; + emit sourceChanged(arg); + startMaskLoading(); + } +} + +void QQuickMaskExtruder::startMaskLoading() +{ + m_pix.clear(this); + if (m_source.isEmpty()) + return; + m_pix.load(qmlEngine(this), m_source); + if (m_pix.isLoading()) + m_pix.connectFinished(this, SLOT(finishMaskLoading())); + else + finishMaskLoading(); +} + +void QQuickMaskExtruder::finishMaskLoading() +{ + if (m_pix.isError()) + qmlInfo(this) << m_pix.error(); +} + +QPointF QQuickMaskExtruder::extrude(const QRectF &r) +{ + ensureInitialized(r); + if (!m_mask.count() || m_img.isNull()) + return r.topLeft(); + const QPointF p = m_mask[rand() % m_mask.count()]; + //### Should random sub-pixel positioning be added? + return p + r.topLeft(); +} + +bool QQuickMaskExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + ensureInitialized(bounds);//###Current usage patterns WILL lead to different bounds/r calls. Separate list? + if (m_img.isNull()) + return false; + QPoint p = point.toPoint() - bounds.topLeft().toPoint(); + return m_img.rect().contains(p) && (bool)m_img.pixelIndex(p); +} + +void QQuickMaskExtruder::ensureInitialized(const QRectF &r) +{ + if (m_lastWidth == r.width() && m_lastHeight == r.height()) + return;//Same as before + if (!m_pix.isReady()) + return; + m_lastWidth = r.width(); + m_lastHeight = r.height(); + + m_mask.clear(); + + m_img = m_pix.image().createAlphaMask(); + m_pix.clear(); + m_img = m_img.convertToFormat(QImage::Format_Mono);//Else LSB, but I think that's easier + m_img = m_img.scaled(r.size().toSize());//TODO: Do they need aspect ratio stuff? Or tiling? + for (int i=0; i<r.width(); i++){ + for (int j=0; j<r.height(); j++){ + if (m_img.pixelIndex(i,j))//Direct bit manipulation is presumably more efficient + m_mask << QPointF(i,j); + } + } +} +QT_END_NAMESPACE diff --git a/src/particles/qquickmaskextruder_p.h b/src/particles/qquickmaskextruder_p.h new file mode 100644 index 0000000000..8b6c3f0b2d --- /dev/null +++ b/src/particles/qquickmaskextruder_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 MASKEXTRUDER_H +#define MASKEXTRUDER_H +#include "qquickparticleextruder_p.h" +#include <private/qquickpixmapcache_p.h> +#include <QUrl> +#include <QImage> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickMaskExtruder : public QQuickParticleExtruder +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) +public: + explicit QQuickMaskExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point); + + QUrl source() const + { + return m_source; + } + +signals: + + void sourceChanged(QUrl arg); + +public slots: + void setSource(QUrl arg); + +private slots: + void startMaskLoading(); + void finishMaskLoading(); + +private: + QUrl m_source; + + void ensureInitialized(const QRectF &r); + int m_lastWidth; + int m_lastHeight; + QQuickPixmap m_pix; + QImage m_img; + QList<QPointF> m_mask;//TODO: More memory efficient datastructures + //Perhaps just the mask for the largest bounds is stored, and interpolate up +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // MASKEXTRUDER_H diff --git a/src/particles/qquickparticleaffector.cpp b/src/particles/qquickparticleaffector.cpp new file mode 100644 index 0000000000..0005af86af --- /dev/null +++ b/src/particles/qquickparticleaffector.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickparticleaffector_p.h" +#include <QDebug> +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Affector QQuickParticleAffector + \inqmlmodule QtQuick.Particles 2 + \brief Affector elements can alter the attributes of logical particles at any point in their lifetime. + + The base Affector does not alter any attributes, but can be used to emit a signal + 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 + This is the system which will be affected by the element. + If the Affector is a direct child of a ParticleSystem, it will automatically be associated with it. +*/ +/*! + \qmlproperty list<string> QtQuick.Particles2::Affector::groups + Which logical particle groups will be affected. + + If empty, it will affect all particles. +*/ +/*! + \qmlproperty list<string> QtQuick.Particles2::Affector::whenCollidingWith + If any logical particle groups are specified here, then the affector + will only be triggered if the particle being examined intersects with + a particle of one of these groups. + + This is different from the groups property. The groups property selects which + particles might be examined, and if they meet other criteria (including being + within the bounds of the Affector, modified by shape) then they will be tested + again to see if they intersect with a particles from one of the particle groups + in whenCollidingWith. + + By default, no groups are specified. +*/ +/*! + \qmlproperty bool QtQuick.Particles2::Affector::enabled + If enabled is set to false, this affector will not affect any particles. + + Usually this is used to conditionally turn an affector on or off. + + Default value is true. +*/ +/*! + \qmlproperty bool QtQuick.Particles2::Affector::once + If once is set to true, this affector will only affect each particle + once in their lifetimes. If the affector normally simulates a continuous + effect over time, then it will simulate the effect of one second of time + the one instant it affects the particle. + + Default value is false. +*/ +/*! + \qmlproperty Shape QtQuick.Particles2::Affector::shape + If a size has been defined, the shape property can be used to affect a + non-rectangular area. +*/ +/*! + \qmlsignal QtQuick.Particles2::Affector::onAffected(x, y) + + This signal is emitted each time the affector actually affects a particle. + + x,y are the coordinates of the affected particle, relative to the ParticleSystem. + +*/ + +/*! + \qmlsignal QtQuick.Particles2::Affector::affectParticle(particle particle, real dt) + + This handler is called when particles are selected to be affected. + + dt is the time since the last time it was affected. Use dt to normalize + trajectory manipulations to real time. + + Note that JS is slower to execute, so it is not recommended to use this in + high-volume particle systems. +*/ +/*! + \qmlsignal QtQuick.Particles2::Affector::affected(real x, real y) + + This handler is called when a particle is selected to be affected. It will + only be called if signal is set to true. + + x,y is the particles current position. +*/ +QQuickParticleAffector::QQuickParticleAffector(QQuickItem *parent) : + QQuickItem(parent), m_needsReset(false), m_ignoresTime(false), m_onceOff(false), m_enabled(true) + , m_system(0), m_updateIntSet(false), m_shape(new QQuickParticleExtruder(this)) +{ +} + +bool QQuickParticleAffector::isAffectedConnected() +{ + static int idx = QObjectPrivate::get(this)->signalIndex("affected(qreal,qreal)"); + return QObjectPrivate::get(this)->isSignalConnected(idx); +} + + +void QQuickParticleAffector::componentComplete() +{ + if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem())) + setSystem(qobject_cast<QQuickParticleSystem*>(parentItem())); + QQuickItem::componentComplete(); +} + +bool QQuickParticleAffector::activeGroup(int g) { + if (m_updateIntSet){ //This can occur before group ids are properly assigned, but that resets the flag + m_groupIds.clear(); + foreach (const QString &p, m_groups) + m_groupIds << m_system->groupIds[p]; + m_updateIntSet = false; + } + return m_groupIds.isEmpty() || m_groupIds.contains(g); +} + +bool QQuickParticleAffector::shouldAffect(QQuickParticleData* d) +{ + if (!d) + return false; + if (activeGroup(d->group)){ + if ((m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index))) + || !d->stillAlive()) + return false; + //Need to have previous location for affected anyways + if (width() == 0 || height() == 0 + || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()), QPointF(d->curX(), d->curY()))){ + if (m_whenCollidingWith.isEmpty() || isColliding(d)){ + return true; + } + } + } + return false; + +} + +void QQuickParticleAffector::postAffect(QQuickParticleData* d) +{ + m_system->needsReset << d; + if (m_onceOff) + m_onceOffed << qMakePair(d->group, d->index); + if (isAffectedConnected()) + emit affected(d->curX(), d->curY()); +} + +const qreal QQuickParticleAffector::simulationDelta = 0.020; +const qreal QQuickParticleAffector::simulationCutoff = 1.000;//If this goes above 1.0, then m_once behaviour needs special codepath + +void QQuickParticleAffector::affectSystem(qreal dt) +{ + if (!m_enabled) + return; + //If not reimplemented, calls affectParticle per particle + //But only on particles in targeted system/area + updateOffsets();//### Needed if an ancestor is transformed. + if (m_onceOff) + dt = 1.0; + foreach (QQuickParticleGroupData* gd, m_system->groupData) { + if (activeGroup(m_system->groupData.key(gd))) { + foreach (QQuickParticleData* 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; + if (d->alive())//Only affect during the parts it was alive for + 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 QQuickParticleAffector::affectParticle(QQuickParticleData *, qreal ) +{ + return true; +} + +void QQuickParticleAffector::reset(QQuickParticleData* pd) +{//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass + if (m_onceOff) + if (activeGroup(pd->group)) + m_onceOffed.remove(qMakePair(pd->group, pd->index)); +} + +void QQuickParticleAffector::updateOffsets() +{ + if (m_system) + m_offset = m_system->mapFromItem(this, QPointF(0, 0)); +} + +bool QQuickParticleAffector::isColliding(QQuickParticleData *d) +{ + qreal myCurX = d->curX(); + qreal myCurY = d->curY(); + qreal myCurSize = d->curSize()/2; + foreach (const QString &group, m_whenCollidingWith){ + foreach (QQuickParticleData* other, m_system->groupData[m_system->groupIds[group]]->data){ + if (!other->stillAlive()) + continue; + qreal otherCurX = other->curX(); + qreal otherCurY = other->curY(); + qreal otherCurSize = other->curSize()/2; + if ((myCurX + myCurSize > otherCurX - otherCurSize + && myCurX - myCurSize < otherCurX + otherCurSize) + && (myCurY + myCurSize > otherCurY - otherCurSize + && myCurY - myCurSize < otherCurY + otherCurSize)) + return true; + } + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickparticleaffector_p.h b/src/particles/qquickparticleaffector_p.h new file mode 100644 index 0000000000..4147488f87 --- /dev/null +++ b/src/particles/qquickparticleaffector_p.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 PARTICLEAFFECTOR_H +#define PARTICLEAFFECTOR_H + +#include <QObject> +#include "qquickparticlesystem_p.h" +#include "qquickparticleextruder_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickParticleAffector : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) + Q_PROPERTY(QStringList whenCollidingWith READ whenCollidingWith WRITE setWhenCollidingWith NOTIFY whenCollidingWithChanged) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool once READ onceOff WRITE setOnceOff NOTIFY onceChanged) + Q_PROPERTY(QQuickParticleExtruder* shape READ shape WRITE setShape NOTIFY shapeChanged) + +public: + explicit QQuickParticleAffector(QQuickItem *parent = 0); + virtual void affectSystem(qreal dt); + virtual void reset(QQuickParticleData*);//As some store their own data per particle? + QQuickParticleSystem* system() const + { + return m_system; + } + + QStringList groups() const + { + return m_groups; + } + + bool enabled() const + { + return m_enabled; + } + + bool onceOff() const + { + return m_onceOff; + } + + QQuickParticleExtruder* shape() const + { + return m_shape; + } + + QStringList whenCollidingWith() const + { + return m_whenCollidingWith; + } + +signals: + + void systemChanged(QQuickParticleSystem* arg); + + void groupsChanged(QStringList arg); + + void enabledChanged(bool arg); + + void onceChanged(bool arg); + + void shapeChanged(QQuickParticleExtruder* arg); + + void affected(qreal x, qreal y); + + void whenCollidingWithChanged(QStringList arg); + +public slots: +void setSystem(QQuickParticleSystem* arg) +{ + if (m_system != arg) { + m_system = arg; + m_system->registerParticleAffector(this); + emit systemChanged(arg); + } +} + +void setGroups(QStringList arg) +{ + if (m_groups != arg) { + m_groups = arg; + m_updateIntSet = true; + emit groupsChanged(arg); + } +} + +void setEnabled(bool arg) +{ + if (m_enabled != arg) { + m_enabled = arg; + emit enabledChanged(arg); + } +} + +void setOnceOff(bool arg) +{ + if (m_onceOff != arg) { + m_onceOff = arg; + m_needsReset = true; + emit onceChanged(arg); + } +} + +void setShape(QQuickParticleExtruder* arg) +{ + if (m_shape != arg) { + m_shape = arg; + emit shapeChanged(arg); + } +} + +void setWhenCollidingWith(QStringList arg) +{ + if (m_whenCollidingWith != arg) { + m_whenCollidingWith = arg; + emit whenCollidingWithChanged(arg); + } +} +public slots: + void updateOffsets(); + +protected: + friend class QQuickParticleSystem; + virtual bool affectParticle(QQuickParticleData *d, qreal dt); + bool m_needsReset:1;//### What is this really saving? + bool m_ignoresTime:1; + bool m_onceOff:1; + bool m_enabled:1; + + QQuickParticleSystem* m_system; + QStringList m_groups; + bool activeGroup(int g); + bool shouldAffect(QQuickParticleData* datum);//Call to do the logic on whether it is affecting that datum + void postAffect(QQuickParticleData* datum);//Call to do the post-affect logic on particles which WERE affected(once off, needs reset, affected signal) + 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; + bool m_updateIntSet; + + QQuickParticleExtruder* m_shape; + + QStringList m_whenCollidingWith; + + bool isColliding(QQuickParticleData* d); +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // PARTICLEAFFECTOR_H diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp new file mode 100644 index 0000000000..035d66cbcd --- /dev/null +++ b/src/particles/qquickparticleemitter.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickparticleemitter_p.h" +#include <private/qqmlengine_p.h> +QT_BEGIN_NAMESPACE + + +/*! + \qmlclass Emitter QQuickParticleEmitter + \inqmlmodule QtQuick.Particles 2 + \brief The Emitter element allows you to emit logical particles. + + This element emits logical particles into the ParticleSystem, with the + given starting attributes. + + Note that logical particles are not + automatically rendered, you will need to have one or more + ParticlePainter elements visualizing them. + + Note that the given starting attributes can be modified at any point + in the particle's lifetime by any Affector element in the same + ParticleSystem. This includes attributes like lifespan. +*/ + + +/*! + \qmlproperty ParticleSystem QtQuick.Particles2::Emitter::system + + This is the Particle system that the Emitter will emit into. + This can be omitted if the Emitter is a direct child of the ParticleSystem +*/ +/*! + \qmlproperty string QtQuick.Particles2::Emitter::group + + This is the logical particle group which it will emit into. + + Default value is "" (empty string). +*/ +/*! + \qmlproperty Shape QtQuick.Particles2::Emitter::shape + + This shape is applied with the size of the Emitter. Particles will be emitted + randomly from any area covered by the shape. + + The default shape is a filled in rectangle, which corresponds to the full bounding + box of the Emitter. +*/ +/*! + \qmlproperty bool QtQuick.Particles2::Emitter::emitting + + If set to false, the emitter will cease emissions until it is set to true. + + Default value is true. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Emitter::emitRate + + Number of particles emitted per second. + + Default value is 10 particles per second. +*/ +/*! + \qmlproperty int QtQuick.Particles2::Emitter::lifeSpan + + The time in milliseconds each emitted particle should last for. + + If you do not want particles to automatically die after a time, for example if + you wish to dispose of them manually, set lifeSpan to Emitter.InfiniteLife. + + lifeSpans greater than or equal to 600000 (10 minutes) will be treated as infinite. + Particles with lifeSpans less than or equal to 0 will start out dead. + + Default value is 1000 (one second). +*/ +/*! + \qmlproperty int QtQuick.Particles2::Emitter::lifeSpanVariation + + Particle lifespans will vary by up to this much in either direction. + + Default value is 0. +*/ + +/*! + \qmlproperty int QtQuick.Particles2::Emitter::maximumEmitted + + The maximum number of particles at a time that this emitter will have alive. + + This can be set as a performance optimization (when using burst and pulse) or + to stagger emissions. + + If this is set to a number below zero, then there is no maximum limit on the number + of particles this emitter can have alive. + + The default value is -1. +*/ +/*! + \qmlproperty int QtQuick.Particles2::Emitter::startTime + + If this value is set when the emitter is loaded, then it will emit particles from the + past, up to startTime milliseconds ago. These will simulate as if they were emitted then, + but will not have any affectors applied to them. Affectors will take effect from the present time. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Emitter::size + + The size in pixels of the particles at the start of their life. + + Default value is 16. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Emitter::endSize + + The size in pixels of the particles at the end of their life. Size will + be linearly interpolated during the life of the particle from this value and + size. If endSize is -1, then the size of the particle will remain constant at + the starting size. + + Default value is -1. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Emitter::sizeVariation + + The size of a particle can vary by this much up or down from size/endSize. The same + random addition is made to both size and endSize for a single particle. + + Default value is 0. +*/ +/*! + \qmlproperty StochasticDirection QtQuick.Particles2::Emitter::speed + + The starting speed of the particles emitted. +*/ +/*! + \qmlproperty StochasticDirection QtQuick.Particles2::Emitter::acceleration + + The starting acceleraton of the particles emitted. +*/ +/*! + \qmlproperty qreal QtQuick.Particles2::Emitter::speedFromMovement + + If this value is non-zero, then any movement of the emitter will provide additional + starting velocity to the particles based on the movement. The additional vector will be the + same angle as the emitter's movement, with a magnitude that is the magnitude of the emitters + movement multiplied by speedFromMovement. + + Default value is 0. +*/ + +/*! + \qmlsignal QtQuick.Particles2::Emitter::onEmitParticles(Array particles) + + This handler is called when particles are emitted. particles is a javascript + array of Particle objects. You can modify particle attributes directly within the handler. + + Note that JS is slower to execute, so it is not recommended to use this in + high-volume particle systems. +*/ + +/*! \qmlmethod QtQuick.Particles2::Emitter::burst(int count) + + Emits count particles from this emitter immediately. +*/ + +/*! \qmlmethod QtQuick.Particles2::Emitter::burst(int x, int y, int count) + + Emits count particles from this emitter immediately. The particles are emitted + as if the Emitter was positioned at x,y but all other properties are the same. +*/ + +/*! \qmlmethod QtQuick.Particles2::Emitter::pulse(int duration) + + If the emitter is not enabled, enables it for duration milliseconds and then switches + it back off. +*/ + +QQuickParticleEmitter::QQuickParticleEmitter(QQuickItem *parent) : + QQuickItem(parent) + , m_particlesPerSecond(10) + , m_particleDuration(1000) + , m_particleDurationVariation(0) + , m_enabled(true) + , m_system(0) + , m_extruder(0) + , m_defaultExtruder(0) + , m_speed(&m_nullVector) + , m_acceleration(&m_nullVector) + , m_particleSize(16) + , m_particleEndSize(-1) + , m_particleSizeVariation(0) + , m_startTime(0) + , m_overwrite(true) + , m_pulseLeft(0) + , m_maxParticleCount(-1) + , m_speed_from_movement(0) + , m_reset_last(true) + , m_last_timestamp(-1) + , m_last_emission(0) + +{ + //TODO: Reset speed/acc back to null vector? Or allow null pointer? + connect(this, SIGNAL(maximumEmittedChanged(int)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); +} + +QQuickParticleEmitter::~QQuickParticleEmitter() +{ + if (m_defaultExtruder) + delete m_defaultExtruder; +} + +bool QQuickParticleEmitter::isEmitConnected() +{ + static int idx = QObjectPrivate::get(this)->signalIndex("emitParticles(QQmlV8Handle)"); + return QObjectPrivate::get(this)->isSignalConnected(idx); +} + +void QQuickParticleEmitter::componentComplete() +{ + if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem())) + setSystem(qobject_cast<QQuickParticleSystem*>(parentItem())); + QQuickItem::componentComplete(); +} + +void QQuickParticleEmitter::setEnabled(bool arg) +{ + if (m_enabled != arg) { + m_enabled = arg; + emit enabledChanged(arg); + } +} + + +QQuickParticleExtruder* QQuickParticleEmitter::effectiveExtruder() +{ + if (m_extruder) + return m_extruder; + if (!m_defaultExtruder) + m_defaultExtruder = new QQuickParticleExtruder; + return m_defaultExtruder; +} + +void QQuickParticleEmitter::pulse(int milliseconds) +{ + if (!m_enabled) + m_pulseLeft = milliseconds; +} + +void QQuickParticleEmitter::burst(int num) +{ + m_burstQueue << qMakePair(num, QPointF(x(), y())); +} + +void QQuickParticleEmitter::burst(int num, qreal x, qreal y) +{ + m_burstQueue << qMakePair(num, QPointF(x, y)); +} + +void QQuickParticleEmitter::setMaxParticleCount(int arg) +{ + if (m_maxParticleCount != arg) { + if (arg < 0 && m_maxParticleCount >= 0){ + connect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); + }else if (arg >= 0 && m_maxParticleCount < 0){ + disconnect(this, SIGNAL(particlesPerSecondChanged(qreal)), + this, SIGNAL(particleCountChanged())); + disconnect(this, SIGNAL(particleDurationChanged(int)), + this, SIGNAL(particleCountChanged())); + } + m_overwrite = arg < 0; + m_maxParticleCount = arg; + emit maximumEmittedChanged(arg); + } +} + +int QQuickParticleEmitter::particleCount() const +{ + if (m_maxParticleCount >= 0) + return m_maxParticleCount; + return m_particlesPerSecond*((m_particleDuration+m_particleDurationVariation)/1000.0); +} + +void QQuickParticleEmitter::setSpeedFromMovement(qreal t) +{ + if (t == m_speed_from_movement) + return; + m_speed_from_movement = t; + emit speedFromMovementChanged(); +} + +void QQuickParticleEmitter::reset() +{ + m_reset_last = true; +} + +void QQuickParticleEmitter::emitWindow(int timeStamp) +{ + if (m_system == 0) + return; + if ((!m_enabled || !m_particlesPerSecond)&& !m_pulseLeft && m_burstQueue.isEmpty()){ + m_reset_last = true; + return; + } + + if (m_reset_last) { + m_last_emitter = m_last_last_emitter = QPointF(x(), y()); + if (m_last_timestamp == -1) + m_last_timestamp = (timeStamp - m_startTime)/1000.; + else + m_last_timestamp = timeStamp/1000.; + m_last_emission = m_last_timestamp; + m_reset_last = false; + m_emitCap = particleCount(); + } + + if (m_pulseLeft){ + m_pulseLeft -= timeStamp - m_last_timestamp * 1000.; + if (m_pulseLeft < 0){ + if (!m_enabled) + timeStamp += m_pulseLeft; + m_pulseLeft = 0; + } + } + qreal time = timeStamp / 1000.; + qreal particleRatio = 1. / m_particlesPerSecond; + qreal pt = m_last_emission; + qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0; + if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now + pt = time - maxLife; + + 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 ax = (m_last_last_emitter.x() + m_last_emitter.x()) / 2; + qreal bx = m_last_emitter.x(); + qreal cx = (x() + m_last_emitter.x()) / 2; + qreal ay = (m_last_last_emitter.y() + m_last_emitter.y()) / 2; + qreal by = m_last_emitter.y(); + qreal cy = (y() + m_last_emitter.y()) / 2; + + 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_pulseLeft && !m_enabled)//'outside time' emissions only + pt = time; + + QList<QQuickParticleData*> toEmit; + + while ((pt < time && m_emitCap) || !m_burstQueue.isEmpty()) { + //int pos = m_last_particle % m_particle_count; + QQuickParticleData* datum = m_system->newDatum(m_system->groupIds[m_group], !m_overwrite); + if (datum){//actually emit(otherwise we've been asked to skip this one) + datum->e = this;//###useful? + qreal t = 1 - (pt - opt) / dt; + qreal vx = + - 2 * ax * (1 - t) + + 2 * bx * (1 - 2 * t) + + 2 * cx * t; + qreal vy = + - 2 * ay * (1 - t) + + 2 * by * (1 - 2 * t) + + 2 * cy * t; + + + // Particle timestamp + datum->t = pt; + datum->lifeSpan = + (m_particleDuration + + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation)) + / 1000.0; + + if (datum->lifeSpan >= m_system->maxLife){ + datum->lifeSpan = m_system->maxLife; + m_emitCap--;//emitCap keeps us from reemitting 'infinite' particles after their life. Unless you reset the emitter. + } + + // 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->vx = speed.x() + + m_speed_from_movement * vx; + datum->vy = speed.y() + + m_speed_from_movement * vy; + + // 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;// * float(m_emitting); + datum->endSize = endSize;// * float(m_emitting); + + toEmit << datum; + } + if (m_burstQueue.isEmpty()){ + pt += particleRatio; + }else{ + m_burstQueue.first().first--; + if (m_burstQueue.first().first <= 0) + m_burstQueue.pop_front(); + } + } + + if (isEmitConnected()) { + v8::HandleScope handle_scope; + v8::Context::Scope scope(QQmlEnginePrivate::getV8Engine(qmlEngine(this))->context()); + v8::Handle<v8::Array> array = v8::Array::New(toEmit.size()); + for (int i=0; i<toEmit.size(); i++) + array->Set(i, toEmit[i]->v8Value().toHandle()); + + emitParticles(QQmlV8Handle::fromHandle(array));//A chance for arbitrary JS changes + } + foreach (QQuickParticleData* d, toEmit) + m_system->emitParticle(d); + + 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/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h new file mode 100644 index 0000000000..eb9e1fd591 --- /dev/null +++ b/src/particles/qquickparticleemitter_p.h @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 PARTICLEEMITTER_H +#define PARTICLEEMITTER_H + +#include <QtQuick/QQuickItem> +#include <QDebug> +#include "qquickparticlesystem_p.h" +#include "qquickparticleextruder_p.h" +#include "qquickdirection_p.h" + +#include <QList> +#include <QPair> +#include <QPointF> +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickParticleEmitter : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QString group READ group WRITE setGroup NOTIFY groupChanged) + Q_PROPERTY(QQuickParticleExtruder* shape READ extruder WRITE setExtruder NOTIFY extruderChanged) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) + + Q_PROPERTY(qreal emitRate READ particlesPerSecond WRITE setParticlesPerSecond NOTIFY particlesPerSecondChanged) + Q_PROPERTY(int lifeSpan READ particleDuration WRITE setParticleDuration NOTIFY particleDurationChanged) + Q_PROPERTY(int lifeSpanVariation READ particleDurationVariation WRITE setParticleDurationVariation NOTIFY particleDurationVariationChanged) + Q_PROPERTY(int maximumEmitted READ maxParticleCount WRITE setMaxParticleCount NOTIFY maximumEmittedChanged) + + Q_PROPERTY(qreal size READ particleSize WRITE setParticleSize NOTIFY particleSizeChanged) + Q_PROPERTY(qreal endSize READ particleEndSize WRITE setParticleEndSize NOTIFY particleEndSizeChanged) + Q_PROPERTY(qreal sizeVariation READ particleSizeVariation WRITE setParticleSizeVariation NOTIFY particleSizeVariationChanged) + + Q_PROPERTY(QQuickDirection *speed READ speed WRITE setSpeed NOTIFY speedChanged) + Q_PROPERTY(QQuickDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) + Q_PROPERTY(qreal speedFromMovement READ speedFromMovement WRITE setSpeedFromMovement NOTIFY speedFromMovementChanged) + + Q_ENUMS(Lifetime) +public: + explicit QQuickParticleEmitter(QQuickItem *parent = 0); + virtual ~QQuickParticleEmitter(); + virtual void emitWindow(int timeStamp); + + enum Lifetime { + InfiniteLife = QQuickParticleSystem::maxLife + }; + + bool enabled() const + { + return m_enabled; + } + + qreal particlesPerSecond() const + { + return m_particlesPerSecond; + } + + int particleDuration() const + { + return m_particleDuration; + } + + QQuickParticleSystem* system() const + { + return m_system; + } + + QString group() const + { + return m_group; + } + + int particleDurationVariation() const + { + return m_particleDurationVariation; + } + + qreal speedFromMovement() const { return m_speed_from_movement; } + void setSpeedFromMovement(qreal s); + virtual void componentComplete(); +signals: + void emitParticles(QQmlV8Handle particles); + void particlesPerSecondChanged(qreal); + void particleDurationChanged(int); + void enabledChanged(bool); + + void systemChanged(QQuickParticleSystem* arg); + + void groupChanged(QString arg); + + void particleDurationVariationChanged(int arg); + + void extruderChanged(QQuickParticleExtruder* arg); + + void particleSizeChanged(qreal arg); + + void particleEndSizeChanged(qreal arg); + + void particleSizeVariationChanged(qreal arg); + + void speedChanged(QQuickDirection * arg); + + void accelerationChanged(QQuickDirection * arg); + + void maximumEmittedChanged(int arg); + void particleCountChanged(); + + void speedFromMovementChanged(); + + void startTimeChanged(int arg); + +public slots: + void pulse(int milliseconds); + void burst(int num); + void burst(int num, qreal x, qreal y); + + void setEnabled(bool arg); + + void setParticlesPerSecond(qreal arg) + { + if (m_particlesPerSecond != arg) { + m_particlesPerSecond = arg; + emit particlesPerSecondChanged(arg); + } + } + + void setParticleDuration(int arg) + { + if (m_particleDuration != arg) { + m_particleDuration = arg; + emit particleDurationChanged(arg); + } + } + + void setSystem(QQuickParticleSystem* arg) + { + if (m_system != arg) { + m_system = arg; + if (m_system) + m_system->registerParticleEmitter(this); + emit systemChanged(arg); + } + } + + void setGroup(QString arg) + { + if (m_group != arg) { + m_group = arg; + emit groupChanged(arg); + } + } + + void setParticleDurationVariation(int arg) + { + if (m_particleDurationVariation != arg) { + m_particleDurationVariation = arg; + emit particleDurationVariationChanged(arg); + } + } + void setExtruder(QQuickParticleExtruder* arg) + { + if (m_extruder != arg) { + m_extruder = arg; + emit extruderChanged(arg); + } + } + + void setParticleSize(qreal arg) + { + if (m_particleSize != arg) { + m_particleSize = arg; + emit particleSizeChanged(arg); + } + } + + void setParticleEndSize(qreal arg) + { + if (m_particleEndSize != arg) { + m_particleEndSize = arg; + emit particleEndSizeChanged(arg); + } + } + + void setParticleSizeVariation(qreal arg) + { + if (m_particleSizeVariation != arg) { + m_particleSizeVariation = arg; + emit particleSizeVariationChanged(arg); + } + } + + void setSpeed(QQuickDirection * arg) + { + if (m_speed != arg) { + m_speed = arg; + emit speedChanged(arg); + } + } + + void setAcceleration(QQuickDirection * arg) + { + if (m_acceleration != arg) { + m_acceleration = arg; + emit accelerationChanged(arg); + } + } + + void setMaxParticleCount(int arg); + + void setStartTime(int arg) + { + if (m_startTime != arg) { + m_startTime = arg; + emit startTimeChanged(arg); + } + } + + virtual void reset(); +public: + int particleCount() const; + + QQuickParticleExtruder* extruder() const + { + return m_extruder; + } + + qreal particleSize() const + { + return m_particleSize; + } + + qreal particleEndSize() const + { + return m_particleEndSize; + } + + qreal particleSizeVariation() const + { + return m_particleSizeVariation; + } + + QQuickDirection * speed() const + { + return m_speed; + } + + QQuickDirection * acceleration() const + { + return m_acceleration; + } + + int maxParticleCount() const + { + return m_maxParticleCount; + } + + int startTime() const + { + return m_startTime; + } + +protected: + qreal m_particlesPerSecond; + int m_particleDuration; + int m_particleDurationVariation; + bool m_enabled; + QQuickParticleSystem* m_system; + QString m_group; + QQuickParticleExtruder* m_extruder; + QQuickParticleExtruder* m_defaultExtruder; + QQuickParticleExtruder* effectiveExtruder(); + QQuickDirection * m_speed; + QQuickDirection * m_acceleration; + qreal m_particleSize; + qreal m_particleEndSize; + qreal m_particleSizeVariation; + + qreal m_speedFromMovement; + int m_startTime; + bool m_overwrite; + + int m_pulseLeft; + QList<QPair<int, QPointF > > m_burstQueue; + int m_maxParticleCount; + + //Used in default implementation, but might be useful + qreal m_speed_from_movement; + + int m_emitCap; + 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; + + bool isEmitConnected(); +private: + QQuickDirection m_nullVector; + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLEEMITTER_H diff --git a/src/particles/qquickparticleextruder.cpp b/src/particles/qquickparticleextruder.cpp new file mode 100644 index 0000000000..279f7c05f3 --- /dev/null +++ b/src/particles/qquickparticleextruder.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickparticleextruder_p.h" +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Shape QQuickParticleExtruder + \inqmlmodule QtQuick.Particles 2 + \brief The Shape element allows you to specify an area for affectors and emitter. + + The base class is just a rectangle. +*/ + +QQuickParticleExtruder::QQuickParticleExtruder(QObject *parent) : + QObject(parent) +{ +} + +QPointF QQuickParticleExtruder::extrude(const QRectF &rect) +{ + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); +} + +bool QQuickParticleExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + return bounds.contains(point); +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickparticleextruder_p.h b/src/particles/qquickparticleextruder_p.h new file mode 100644 index 0000000000..e24950e4e8 --- /dev/null +++ b/src/particles/qquickparticleextruder_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 PARTICLEEXTRUDER_H +#define PARTICLEEXTRUDER_H + +#include <QObject> +#include <QRectF> +#include <QPointF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickParticleExtruder : public QObject +{ + Q_OBJECT + +public: + explicit QQuickParticleExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point); + +signals: +public slots: +protected: +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLEEXTRUDER_H diff --git a/src/particles/qquickparticlegroup.cpp b/src/particles/qquickparticlegroup.cpp new file mode 100644 index 0000000000..9a165074a1 --- /dev/null +++ b/src/particles/qquickparticlegroup.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickparticlegroup_p.h" + +/*! + \qmlclass ParticleGroup QQuickParticleGroup + \inqmlmodule QtQuick.Particles 2 + \brief ParticleGroup elements allow you to set attributes on a logical particle group. + + This element allows you to set timed transitions on particle groups. + + You can also use this element to group particle system elements related to the logical + particle group. Emitters, Affectors and Painters set as direct children of a ParticleGroup + will automatically apply to that logical particle group. TrailEmitters will automatically follow + the group. + + If a ParticleGroup element is not defined for a group, the group will function normally as if + none of the transition properties were set. +*/ +/*! + \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::system + This is the system which will contain the group. + + If the ParticleGroup is a direct child of a ParticleSystem, it will automatically be associated with it. +*/ +/*! + \qmlproperty string QtQuick.Particles2::ParticleGroup::name + This is the name of the particle group, and how it is generally referred to by other elements. + + If elements refer to a name which does not have an explicit ParticleGroup created, it will + work normally (with no transitions specified for the group). If you do not need to assign + duration based transitions to a group, you do not need to create a ParticleGroup with that name (although you may). +*/ +/*! + \qmlproperty int QtQuick.Particles2::ParticleGroup::duration + The time in milliseconds before the group will attempt to transition. + +*/ +/*! + \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::durationVariation + The maximum number of milliseconds that the duration of the transition cycle varies per particle in the group. + + Default value is zero. +*/ +/*! + \qmlproperty ParticleSystem QtQuick.Particles2::ParticleGroup::to + The weighted list of transitions valid for this group. + + If the chosen transition stays in this group, another duration (+/- up to durationVariation) + milliseconds will occur before another transition is attempted. +*/ + +QQuickParticleGroup::QQuickParticleGroup(QObject* parent) + : QQuickStochasticState(parent) + , m_system(0) +{ + +} + +void delayedRedirect(QQmlListProperty<QObject> *prop, QObject *value) +{ + QQuickParticleGroup* pg = qobject_cast<QQuickParticleGroup*>(prop->object); + if (pg) + pg->delayRedirect(value); +} + +QQmlListProperty<QObject> QQuickParticleGroup::particleChildren() +{ + QQuickParticleSystem* system = qobject_cast<QQuickParticleSystem*>(parent()); + if (system) + return QQmlListProperty<QObject>(this, 0, &QQuickParticleSystem::statePropertyRedirect); + else + return QQmlListProperty<QObject>(this, 0, &delayedRedirect); +} + +void QQuickParticleGroup::setSystem(QQuickParticleSystem* arg) +{ + if (m_system != arg) { + m_system = arg; + m_system->registerParticleGroup(this); + performDelayedRedirects(); + emit systemChanged(arg); + } +} + +void QQuickParticleGroup::delayRedirect(QObject *obj) +{ + m_delayedRedirects << obj; +} + +void QQuickParticleGroup::performDelayedRedirects() +{ + if (!m_system) + return; + foreach (QObject* obj, m_delayedRedirects) + m_system->stateRedirect(this, m_system, obj); + + m_delayedRedirects.clear(); +} + +void QQuickParticleGroup::componentComplete(){ + if (!m_system && qobject_cast<QQuickParticleSystem*>(parent())) + setSystem(qobject_cast<QQuickParticleSystem*>(parent())); +} diff --git a/src/particles/qquickparticlegroup_p.h b/src/particles/qquickparticlegroup_p.h new file mode 100644 index 0000000000..8889187558 --- /dev/null +++ b/src/particles/qquickparticlegroup_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 QQuickPARTICLEGROUP +#define QQuickPARTICLEGROUP +#include <private/qquickspriteengine_p.h> +#include "qquickparticlesystem_p.h" +#include "qqmlparserstatus.h" + +QT_BEGIN_NAMESPACE + +class QQuickParticleGroup : public QQuickStochasticState, public QQmlParserStatus +{ + Q_OBJECT + //### Would setting limits per group be useful? Or clutter the API? + //Q_PROPERTY(int maximumAlive READ maximumAlive WRITE setMaximumAlive NOTIFY maximumAliveChanged) + + Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + + //Intercept children requests and assign to the group & system + Q_PROPERTY(QQmlListProperty<QObject> particleChildren READ particleChildren DESIGNABLE false)//### Hidden property for in-state system definitions - ought not to be used in actual "Sprite" states + Q_CLASSINFO("DefaultProperty", "particleChildren") + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QQuickParticleGroup(QObject* parent = 0); + + QQmlListProperty<QObject> particleChildren(); + + int maximumAlive() const + { + return m_maximumAlive; + } + + QQuickParticleSystem* system() const + { + return m_system; + } + +public slots: + + void setMaximumAlive(int arg) + { + if (m_maximumAlive != arg) { + m_maximumAlive = arg; + emit maximumAliveChanged(arg); + } + } + + void setSystem(QQuickParticleSystem* arg); + + void delayRedirect(QObject* obj); + +signals: + + void maximumAliveChanged(int arg); + + void systemChanged(QQuickParticleSystem* arg); + +protected: + virtual void componentComplete(); + virtual void classBegin(){;} + +private: + + void performDelayedRedirects(); + + int m_maximumAlive; + QQuickParticleSystem* m_system; + QList<QObject*> m_delayedRedirects; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/particles/qquickparticlepainter.cpp b/src/particles/qquickparticlepainter.cpp new file mode 100644 index 0000000000..e490b70240 --- /dev/null +++ b/src/particles/qquickparticlepainter.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickparticlepainter_p.h" +#include <QQuickCanvas> +#include <QDebug> +QT_BEGIN_NAMESPACE +/*! + \qmlclass ParticlePainter QQuickParticlePainter + \inqmlmodule QtQuick.Particles 2 + \inherits ParticlePainter + \brief ParticlePainter elements allow you to specify how to paint particles. + + The default implementation paints nothing. See the subclasses if you want to + paint something visible. + +*/ +/*! + \qmlproperty ParticleSystem QtQuick.Particles2::ParticlePainter::system + This is the system whose particles can be painted by the element. + If the ParticlePainter is a direct child of a ParticleSystem, it will automatically be associated with it. +*/ +/*! + \qmlproperty list<string> QtQuick.Particles2::ParticlePainter::groups + Which logical particle groups will be painted. + + If empty, it will paint the default particle group (""). +*/ +QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent) : + QQuickItem(parent), + m_system(0), m_count(0), m_pleaseReset(true), m_canvas(0) +{ +} + +void QQuickParticlePainter::itemChange(ItemChange change, const ItemChangeData &data) +{ + if (change == QQuickItem::ItemSceneChange) { + if (m_canvas) + disconnect(m_canvas, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated())); + m_canvas = data.canvas; + if (m_canvas) + connect(m_canvas, SIGNAL(sceneGraphInvalidated()), this, SLOT(sceneGraphInvalidated()), Qt::DirectConnection); + + } +} + +void QQuickParticlePainter::componentComplete() +{ + if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem())) + setSystem(qobject_cast<QQuickParticleSystem*>(parentItem())); + QQuickItem::componentComplete(); +} + + +void QQuickParticlePainter::setSystem(QQuickParticleSystem *arg) +{ + if (m_system != arg) { + m_system = arg; + if (m_system){ + m_system->registerParticlePainter(this); + reset(); + } + emit systemChanged(arg); + } +} + +void QQuickParticlePainter::load(QQuickParticleData* d) +{ + initialize(d->group, d->index); + if (m_pleaseReset) + return; + m_pendingCommits << qMakePair<int, int>(d->group, d->index); +} + +void QQuickParticlePainter::reload(QQuickParticleData* d) +{ + if (m_pleaseReset) + return; + m_pendingCommits << qMakePair<int, int>(d->group, d->index); +} + +void QQuickParticlePainter::reset() +{ + m_pendingCommits.clear(); + m_pleaseReset = true; +} + +void QQuickParticlePainter::setCount(int c)//### TODO: some resizeing so that particles can reallocate on size change instead of recreate +{ + Q_ASSERT(c >= 0); //XXX + if (c == m_count) + return; + m_count = c; + emit countChanged(); + reset(); +} + +int QQuickParticlePainter::count() +{ + return m_count; +} + +void QQuickParticlePainter::calcSystemOffset(bool resetPending) +{ + if (!m_system || !parentItem()) + return; + QPointF lastOffset = m_systemOffset; + m_systemOffset = -1 * this->mapFromItem(m_system, QPointF(0.0, 0.0)); + if (lastOffset != m_systemOffset && !resetPending){ + //Reload all particles//TODO: Necessary? + foreach (const QString &g, m_groups){ + int gId = m_system->groupIds[g]; + foreach (QQuickParticleData* d, m_system->groupData[gId]->data) + reload(d); + } + } +} +typedef QPair<int,int> intPair; +void QQuickParticlePainter::performPendingCommits() +{ + calcSystemOffset(); + foreach (intPair p, m_pendingCommits) + commit(p.first, p.second); + m_pendingCommits.clear(); +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickparticlepainter_p.h b/src/particles/qquickparticlepainter_p.h new file mode 100644 index 0000000000..1ae4625856 --- /dev/null +++ b/src/particles/qquickparticlepainter_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 PARTICLE_H +#define PARTICLE_H + +#include <QObject> +#include <QDebug> +#include <QPair> +#include "qquickparticlesystem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickParticlePainter : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) + Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) + +public: + explicit QQuickParticlePainter(QQuickItem *parent = 0); + //Data Interface to system + void load(QQuickParticleData*); + void reload(QQuickParticleData*); + void setCount(int c); + int count(); + void performPendingCommits();//Called from updatePaintNode + QQuickParticleSystem* system() const + { + return m_system; + } + + + QStringList groups() const + { + return m_groups; + } + + void itemChange(ItemChange, const ItemChangeData &); + +signals: + void countChanged(); + void systemChanged(QQuickParticleSystem* arg); + + void groupsChanged(QStringList arg); + +public slots: + void setSystem(QQuickParticleSystem* arg); + + void setGroups(QStringList arg) + { + if (m_groups != arg) { + m_groups = arg; + //Note: The system watches this as it has to recalc things when groups change. It will request a reset if necessary + emit groupsChanged(arg); + } + } + + void calcSystemOffset(bool resetPending = false); + +private slots: + virtual void sceneGraphInvalidated() {} + +protected: + /* Reset resets all your internal data structures. But anything attached to a particle should + be in attached data. So reset + reloads should have no visible effect. + ###Hunt down all cases where we do a complete reset for convenience and be more targeted + */ + virtual void reset(); + + virtual void componentComplete(); + virtual void initialize(int gIdx, int pIdx){//Called from main thread + Q_UNUSED(gIdx); + Q_UNUSED(pIdx); + } + virtual void commit(int gIdx, int pIdx){//Called in Render Thread + //###If you need to do something on size changed, check m_data size in this? Or we reset you every time? + Q_UNUSED(gIdx); + Q_UNUSED(pIdx); + } + + QQuickParticleSystem* m_system; + friend class QQuickParticleSystem; + int m_count; + bool m_pleaseReset;//Used by subclasses, but it's a nice optimization to know when stuff isn't going to matter. + QStringList m_groups; + QPointF m_systemOffset; + + QQuickCanvas *m_canvas; + +private: + QSet<QPair<int,int> > m_pendingCommits; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // PARTICLE_H diff --git a/src/particles/qquickparticlesmodule.cpp b/src/particles/qquickparticlesmodule.cpp new file mode 100644 index 0000000000..e83cde3cd1 --- /dev/null +++ b/src/particles/qquickparticlesmodule.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickangledirection_p.h" +#include "qquickcustomparticle_p.h" +#include "qquickellipseextruder_p.h" +#include "qquicktrailemitter_p.h" +#include "qquickfriction_p.h" +#include "qquickgravity_p.h" +#include "qquickimageparticle_p.h" +#include "qquickitemparticle_p.h" +#include "qquickage_p.h" +#include "qquicklineextruder_p.h" +#include "qquickmaskextruder_p.h" +#include "qquickparticleaffector_p.h" +#include "qquickparticleemitter_p.h" +#include "qquickparticleextruder_p.h" +#include "qquickparticlepainter_p.h" +#include "qquickparticlesmodule_p.h" +#include "qquickparticlesystem_p.h" +#include "qquickpointattractor_p.h" +#include "qquickpointdirection_p.h" +#include "qquickspritegoal_p.h" +#include "qquickdirection_p.h" +#include "qquicktargetdirection_p.h" +#include "qquickturbulence_p.h" +#include "qquickwander_p.h" +#include "qquickcumulativedirection_p.h" +#include "qquickcustomaffector_p.h" +#include "qquickrectangleextruder_p.h" +#include "qquickparticlegroup_p.h" +#include "qquickgroupgoal_p.h" + +QT_BEGIN_NAMESPACE + +void QQuickParticlesModule::defineModule() +{ + const char* uri = "QtQuick.Particles"; + + qmlRegisterType<QQuickParticleSystem>(uri, 2, 0, "ParticleSystem"); + qmlRegisterType<QQuickParticleGroup>(uri, 2, 0, "ParticleGroup"); + + qmlRegisterType<QQuickImageParticle>(uri, 2, 0, "ImageParticle"); + qmlRegisterType<QQuickCustomParticle>(uri, 2, 0, "CustomParticle"); + qmlRegisterType<QQuickItemParticle>(uri, 2, 0, "ItemParticle"); + + qmlRegisterType<QQuickParticleEmitter>(uri, 2, 0, "Emitter"); + qmlRegisterType<QQuickTrailEmitter>(uri, 2, 0, "TrailEmitter"); + + qmlRegisterType<QQuickEllipseExtruder>(uri, 2, 0, "EllipseShape"); + qmlRegisterType<QQuickRectangleExtruder>(uri, 2, 0, "RectangleShape"); + qmlRegisterType<QQuickLineExtruder>(uri, 2, 0, "LineShape"); + qmlRegisterType<QQuickMaskExtruder>(uri, 2, 0, "MaskShape"); + + qmlRegisterType<QQuickPointDirection>(uri, 2, 0, "PointDirection"); + qmlRegisterType<QQuickAngleDirection>(uri, 2, 0, "AngleDirection"); + qmlRegisterType<QQuickTargetDirection>(uri, 2, 0, "TargetDirection"); + qmlRegisterType<QQuickCumulativeDirection>(uri, 2, 0, "CumulativeDirection"); + + qmlRegisterType<QQuickCustomAffector>(uri, 2, 0, "Affector"); + qmlRegisterType<QQuickWanderAffector>(uri, 2, 0, "Wander"); + qmlRegisterType<QQuickFrictionAffector>(uri, 2, 0, "Friction"); + qmlRegisterType<QQuickAttractorAffector>(uri, 2, 0, "Attractor"); + qmlRegisterType<QQuickGravityAffector>(uri, 2, 0, "Gravity"); + qmlRegisterType<QQuickAgeAffector>(uri, 2, 0, "Age"); + qmlRegisterType<QQuickSpriteGoalAffector>(uri, 2, 0, "SpriteGoal"); + qmlRegisterType<QQuickGroupGoalAffector>(uri, 2, 0, "GroupGoal"); + qmlRegisterType<QQuickTurbulenceAffector>(uri, 2, 0 , "Turbulence"); + + //Exposed just for completeness + qmlRegisterUncreatableType<QQuickParticleAffector>(uri, 2, 0, "ParticleAffector", + QStringLiteral("Abstract type. Use one of the inheriting types instead.")); + qmlRegisterUncreatableType<QQuickParticlePainter>(uri, 2, 0, "ParticlePainter", + QStringLiteral("Abstract type. Use one of the inheriting types instead.")); + qmlRegisterUncreatableType<QQuickParticleExtruder>(uri, 2, 0, "ParticleExtruder", + QStringLiteral("Abstract type. Use one of the inheriting types instead.")); + qmlRegisterUncreatableType<QQuickDirection>(uri, 2, 0, "NullVector", + QStringLiteral("Abstract type. Use one of the inheriting types instead.")); +} + +QT_END_NAMESPACE + diff --git a/src/particles/qquickparticlesmodule_p.h b/src/particles/qquickparticlesmodule_p.h new file mode 100644 index 0000000000..87e99fa6bb --- /dev/null +++ b/src/particles/qquickparticlesmodule_p.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 QQUICKPARTICLESMODULE_H +#define QQUICKPARTICLESMODULE_H + +#include <private/qtquickparticlesglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticlesModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKPARTICLESMODULE_H diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp new file mode 100644 index 0000000000..4fd6108f2f --- /dev/null +++ b/src/particles/qquickparticlesystem.cpp @@ -0,0 +1,1164 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 <QtQuick/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/qqmlengine_p.h> +#include <private/qqmlglobal_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) + + +/* \internal ParticleSystem internals documentation + + Affectors, Painters, Emitters and Groups all register themselves on construction as a callback + from their setSystem (or componentComplete if they have a system from a parent). + + Particle data is stored by group, They have a group index (used by the particle system almost + everywhere) and a global index (used by the Stochastic state engine powering stochastic group + transitions). Each group has a recycling list/heap that stores the particle data. + + The recycling list/heap is a heap of particle data sorted by when they're expected to die. If + they die prematurely then they are marked as reusable (and will probably still be alive when + they exit the heap). If they have their life extended, then they aren't dead when expected. + If this happens, they go back in the heap with the new estimate. If they have died on schedule, + then the indexes are marked as reusable. If no indexes are reusable when new particles are + requested, then the list is extended. This relatively complex datastructure is because memory + allocation and deallocation on this scale proved to be a significant performance cost. In order + to reuse the indexes validly (even when particles can have their life extended or cut short + dynamically, or particle counts grow) this seemed to be the most efficient option for keeping + track of which indices could be reused. + + When a new particle is emitted, the emitter gets a new datum from the group (through the + system), and sets properties on it. Then it's passed back to the group briefly so that it can + now guess when the particle will die. Then the painters get a change to initialize properties + as well, since particle data includes shared data from painters as well as logical particle + data. + + Every animation advance, the simulation advances by running all emitters for the elapsed + duration, then running all affectors, then telling all particle painters to update changed + particles. The ParticlePainter superclass stores these changes, and they are implemented + when the painter is called to paint in the render thread. + + Particle group changes move the particle from one group to another by killing the old particle + and then creating a new one with the same data in the new group. + + Note that currently groups only grow. Given that data is stored in vectors, it is non-trivial + to pluck out the unused indexes when the count goes down. Given the dynamic nature of the + system, it is difficult to tell if those unused data instances will be used again. Still, + some form of garbage collection is on the long term plan. +*/ + +/*! + \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; + frameAt = -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; +} + +QQmlV8Handle QQuickParticleData::v8Value() +{ + if (!v8Datum) + v8Datum = new QQuickV8ParticleData(QQmlEnginePrivate::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_animation(0), + m_running(true), + initialized(0), + particleCount(0), + m_nextIndex(0), + m_componentComplete(false), + m_paused(false), + m_empty(true) +{ + 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) +{ + if (m_debugMode) + qDebug() << "Registering Painter" << p << "to" << this; + //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) +{ + if (m_debugMode) + qDebug() << "Registering Emitter" << e << "to" << this; + 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) +{ + if (m_debugMode) + qDebug() << "Registering Affector" << a << "to" << this; + m_affectors << QPointer<QQuickParticleAffector>(a); +} + +void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g) +{ + if (m_debugMode) + qDebug() << "Registering Group" << g << "to" << this; + 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(QQmlListProperty<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 || !p) + 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 (QQuickParticleAffector *a, m_affectors)//Groups may have changed + a->m_updateIntSet = true; + + 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 diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h new file mode 100644 index 0000000000..f70cc5af94 --- /dev/null +++ b/src/particles/qquickparticlesystem_p.h @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 PARTICLESYSTEM_H +#define PARTICLESYSTEM_H + +#include <QtQuick/QQuickItem> +#include <QElapsedTimer> +#include <QVector> +#include <QHash> +#include <QPointer> +#include <QSignalMapper> +#include <private/qquicksprite_p.h> +#include <QAbstractAnimation> +#include <QtQml/qqml.h> +#include <private/qv8engine_p.h> //For QQmlV8Handle + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickParticleSystem; +class QQuickParticleAffector; +class QQuickParticleEmitter; +class QQuickParticlePainter; +class QQuickParticleData; +class QQuickParticleSystemAnimation; +class QQuickStochasticEngine; +class QQuickSprite; +class QQuickV8ParticleData; +class QQuickParticleGroup; +class QQuickImageParticle; + +struct QQuickParticleDataHeapNode{ + int time;//in ms + QSet<QQuickParticleData*> data;//Set ptrs instead? +}; + +class QQuickParticleDataHeap { + //Idea is to do a binary heap, but which also stores a set of int,Node* so that if the int already exists, you can + //add it to the data* list. Pops return the whole list at once. +public: + QQuickParticleDataHeap(); + void insert(QQuickParticleData* data); + void insertTimed(QQuickParticleData* data, int time); + + int top(); + + QSet<QQuickParticleData*> pop(); + + void clear(); + + bool contains(QQuickParticleData*);//O(n), for debugging purposes only +private: + void grow(); + void swap(int, int); + void bubbleUp(int); + void bubbleDown(int); + int m_size; + int m_end; + QQuickParticleDataHeapNode m_tmp; + QVector<QQuickParticleDataHeapNode> m_data; + QHash<int,int> m_lookups; +}; + +class Q_AUTOTEST_EXPORT QQuickParticleGroupData { +public: + QQuickParticleGroupData(int id, QQuickParticleSystem* sys); + ~QQuickParticleGroupData(); + + int size(); + QString name(); + + void setSize(int newSize); + + int index; + QSet<QQuickParticlePainter*> painters;//TODO: What if they are dynamically removed? + + //TODO: Refactor particle data list out into a separate class + QVector<QQuickParticleData*> data; + QQuickParticleDataHeap dataHeap; + QSet<int> reusableIndexes; + bool recycle(); //Force recycling round, returns true if all indexes are now reusable + + void initList(); + void kill(QQuickParticleData* d); + + //After calling this, initialize, then call prepareRecycler(d) + QQuickParticleData* newDatum(bool respectsLimits); + + //TODO: Find and clean up those that don't get added to the recycler (currently they get lost) + void prepareRecycler(QQuickParticleData* d); + +private: + int m_size; + QQuickParticleSystem* m_system; +}; + +struct Color4ub { + uchar r; + uchar g; + uchar b; + uchar a; +}; + +class Q_AUTOTEST_EXPORT QQuickParticleData { +public: + //TODO: QObject like memory management (without the cost, just attached to system) + QQuickParticleData(QQuickParticleSystem* sys); + ~QQuickParticleData(); + + //Convenience functions for working backwards, because parameters are from the start of particle life + //If setting multiple parameters at once, doing the conversion yourself will be faster. + + //sets the x accleration without affecting the instantaneous x velocity or position + void setInstantaneousAX(qreal ax); + //sets the x velocity without affecting the instantaneous x postion + void setInstantaneousVX(qreal vx); + //sets the instantaneous x postion + void setInstantaneousX(qreal x); + //sets the y accleration without affecting the instantaneous y velocity or position + void setInstantaneousAY(qreal ay); + //sets the y velocity without affecting the instantaneous y postion + void setInstantaneousVY(qreal vy); + //sets the instantaneous Y postion + void setInstantaneousY(qreal y); + + //TODO: Slight caching? + qreal curX() const; + qreal curVX() const; + qreal curAX() const { return ax; } + qreal curY() const; + qreal curVY() const; + qreal curAY() const { return ay; } + + int group; + QQuickParticleEmitter* e;//### Needed? + QQuickParticleSystem* system; + int index; + int systemIndex; + + //General Position Stuff + float x; + float y; + float t; + float lifeSpan; + float size; + float endSize; + float vx; + float vy; + float ax; + float ay; + + //Other stuff, now universally shared + Color4ub color; + float xx; + float xy; + float yx; + float yy; + float rotation; + float rotationSpeed; + float autoRotate;//Assume that GPUs prefer floats to bools + float animIdx; + float frameDuration; + float frameAt;//Used for duration -1 + float frameCount; + float animT; + float animX; + float animY; + float animWidth; + float animHeight; + float r; + QQuickItem* delegate; + int modelIndex; + float update;//Used by custom affectors + + //Used by image particle + QQuickImageParticle* colorOwner; + QQuickImageParticle* rotationOwner; + QQuickImageParticle* deformationOwner; + QQuickImageParticle* animationOwner; + + void debugDump(); + bool stillAlive();//Only checks end, because usually that's all you need and it's a little faster. + bool alive(); + float lifeLeft(); + float curSize(); + void clone(const QQuickParticleData& other);//Not =, leaves meta-data like index + QQmlV8Handle v8Value(); + void extendLife(float time); +private: + QQuickV8ParticleData* v8Datum; +}; + +class Q_AUTOTEST_EXPORT QQuickParticleSystem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) + Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged) + +public: + explicit QQuickParticleSystem(QQuickItem *parent = 0); + ~QQuickParticleSystem(); + + bool isRunning() const + { + return m_running; + } + + int count(){ return particleCount; } + + static const int maxLife = 600000; + +signals: + + void systemInitialized(); + void runningChanged(bool arg); + void pausedChanged(bool arg); + void emptyChanged(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); + + virtual int duration() const { return -1; } + + +protected: + //This one only once per frame (effectively) + void componentComplete(); + +private slots: + void emittersChanged(); + void loadPainter(QObject* p); + void createEngine(); //Not invoked by sprite engine, unlike Sprite uses + void particleStateChange(int idx); + +public: + //These can be called multiple times per frame, performance critical + void emitParticle(QQuickParticleData* p); + QQuickParticleData* newDatum(int groupId, bool respectLimits = true, int sysIdx = -1); + void finishNewDatum(QQuickParticleData*); + void moveGroups(QQuickParticleData *d, int newGIdx); + int nextSystemIndex(); + + //This one only once per painter per frame + int systemSync(QQuickParticlePainter* p); + + //Data members here for ease of related class and auto-test usage. Not "public" API. TODO: d_ptrize + QSet<QQuickParticleData*> needsReset; + QVector<QQuickParticleData*> bySysIdx; //Another reference to the data (data owned by group), but by sysIdx + QHash<QString, int> groupIds; + QHash<int, QQuickParticleGroupData*> groupData; + QQuickStochasticEngine* stateEngine; + + //Also only here for auto-test usage + void updateCurrentTime( int currentTime ); + QQuickParticleSystemAnimation* m_animation; + bool m_running; + bool m_debugMode; + + int timeInt; + bool initialized; + int particleCount; + + void registerParticlePainter(QQuickParticlePainter* p); + void registerParticleEmitter(QQuickParticleEmitter* e); + void registerParticleAffector(QQuickParticleAffector* a); + void registerParticleGroup(QQuickParticleGroup* g); + + static void statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value); + static void stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value); + bool isPaused() const + { + return m_paused; + } + + bool isEmpty() const + { + return m_empty; + } + +private: + void initializeSystem(); + void initGroups(); + QList<QPointer<QQuickParticleEmitter> > m_emitters; + QList<QPointer<QQuickParticleAffector> > m_affectors; + QList<QPointer<QQuickParticlePainter> > m_painters; + QList<QPointer<QQuickParticlePainter> > m_syncList; + QList<QQuickParticleGroup*> m_groups; + int m_nextGroupId; + int m_nextIndex; + QSet<int> m_reusableIndexes; + bool m_componentComplete; + + QSignalMapper m_painterMapper; + QSignalMapper m_emitterMapper; + bool m_paused; + bool m_allDead; + bool m_empty; +}; + +// Internally, this animation drives all the timing. Painters sync up in their updatePaintNode +class QQuickParticleSystemAnimation : public QAbstractAnimation +{ + Q_OBJECT +public: + QQuickParticleSystemAnimation(QQuickParticleSystem* system) + : QAbstractAnimation(static_cast<QObject*>(system)), m_system(system) + { } +protected: + virtual void updateCurrentTime( int t ) + { + m_system->updateCurrentTime(t); + } + + virtual int duration() const + { + return -1; + } + +private: + QQuickParticleSystem* m_system; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PARTICLESYSTEM_H + + diff --git a/src/particles/qquickpointattractor.cpp b/src/particles/qquickpointattractor.cpp new file mode 100644 index 0000000000..a54ee41c1e --- /dev/null +++ b/src/particles/qquickpointattractor.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickpointattractor_p.h" +#include <cmath> +#include <QDebug> +QT_BEGIN_NAMESPACE +/*! + \qmlclass Attractor QQuickAttractorAffector + \inqmlmodule QtQuick.Particles 2 + \inherits Affector + \brief The Attractor allows you to attract particles towards a specific point. + + Note that the size and position of this element affects which particles it affects. + The size of the point attracted to is always 0x0, and the location of that point + is specified by the pointX and pointY properties. + + Note that Attractor has the standard Item x,y,width and height properties. + Like other affectors, these represent the affected area. They + do not represent the 0x0 point which is the target of the attraction. +*/ + + +/*! + \qmlproperty real QtQuick.Particles2::PointAttractor::pointX + + The x coordinate of the attracting point. This is relative + to the x coordinate of the Attractor. +*/ +/*! + \qmlproperty real QtQuick.Particles2::PointAttractor::pointY + + The y coordinate of the attracting point. This is relative + to the y coordinate of the Attractor. +*/ +/*! + \qmlproperty real QtQuick.Particles2::PointAttractor::strength + + The pull, in units per second, to be exerted on an item one pixel away. + + Depending on how the attraction is proportionalToDistance this may have to + be very high or very low to have a reasonable effect on particles at a + distance. +*/ +/*! + \qmlproperty AffectableParameter QtQuick.Particles2::Attractor::affectedParameter + + What attribute of particles is directly affected. + \list + \li Attractor.Position + \li Attractor.Velocity + \li Attractor.Acceleration + \endlist +*/ +/*! + \qmlproperty Proportion QtQuick.Particles2::Attractor::proportionalToDistance + + How the distance from the particle to the point affects the strength of the attraction. + + \list + \li Attractor.Constant + \li Attractor.Linear + \li Attractor.InverseLinear + \li Attractor.Quadratic + \li Attractor.InverseQuadratic + \endlist +*/ + + +QQuickAttractorAffector::QQuickAttractorAffector(QQuickItem *parent) : + QQuickParticleAffector(parent), m_strength(0.0), m_x(0), m_y(0) + , m_physics(Velocity), m_proportionalToDistance(Linear) +{ +} + +bool QQuickAttractorAffector::affectParticle(QQuickParticleData *d, qreal dt) +{ + if (m_strength == 0.0) + return false; + qreal dx = m_x+m_offset.x() - d->curX(); + qreal dy = m_y+m_offset.y() - d->curY(); + qreal r = sqrt((dx*dx) + (dy*dy)); + qreal theta = atan2(dy,dx); + qreal ds = 0; + switch (m_proportionalToDistance){ + case InverseQuadratic: + ds = (m_strength / qMax<qreal>(1.,r*r)); + break; + case InverseLinear: + ds = (m_strength / qMax<qreal>(1.,r)); + break; + case Quadratic: + ds = (m_strength * qMax<qreal>(1.,r*r)); + break; + case Linear: + ds = (m_strength * qMax<qreal>(1.,r)); + break; + default: //also Constant + ds = m_strength; + } + ds *= dt; + dx = ds * cos(theta); + dy = ds * sin(theta); + qreal vx,vy; + switch (m_physics){ + case Position: + d->x = (d->x + dx); + d->y = (d->y + dy); + break; + case Acceleration: + d->setInstantaneousAX(d->ax + dx); + d->setInstantaneousAY(d->ay + dy); + break; + case Velocity: //also default + default: + vx = d->curVX(); + vy = d->curVY(); + d->setInstantaneousVX(vx + dx); + d->setInstantaneousVY(vy + dy); + } + + return true; +} +QT_END_NAMESPACE diff --git a/src/particles/qquickpointattractor_p.h b/src/particles/qquickpointattractor_p.h new file mode 100644 index 0000000000..85b7a9aa30 --- /dev/null +++ b/src/particles/qquickpointattractor_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 ATTRACTORAFFECTOR_H +#define ATTRACTORAFFECTOR_H +#include "qquickparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickAttractorAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) + Q_PROPERTY(qreal pointX READ pointX WRITE setPointX NOTIFY pointXChanged) + Q_PROPERTY(qreal pointY READ pointY WRITE setPointY NOTIFY pointYChanged) + Q_PROPERTY(AffectableParameters affectedParameter READ affectedParameter WRITE setAffectedParameter NOTIFY affectedParameterChanged) + Q_PROPERTY(Proportion proportionalToDistance READ proportionalToDistance WRITE setProportionalToDistance NOTIFY proportionalToDistanceChanged) + Q_ENUMS(AffectableParameters) + Q_ENUMS(Proportion) + +public: + enum Proportion{ + Constant, + Linear, + Quadratic, + InverseLinear, + InverseQuadratic + }; + + enum AffectableParameters { + Position, + Velocity, + Acceleration + }; + + explicit QQuickAttractorAffector(QQuickItem *parent = 0); + + qreal strength() const + { + return m_strength; + } + + qreal pointX() const + { + return m_x; + } + + qreal pointY() const + { + return m_y; + } + + AffectableParameters affectedParameter() const + { + return m_physics; + } + + Proportion proportionalToDistance() const + { + return m_proportionalToDistance; + } + +signals: + + void strengthChanged(qreal arg); + + void pointXChanged(qreal arg); + + void pointYChanged(qreal arg); + + void affectedParameterChanged(AffectableParameters arg); + + void proportionalToDistanceChanged(Proportion arg); + +public slots: +void setStrength(qreal arg) +{ + if (m_strength != arg) { + m_strength = arg; + emit strengthChanged(arg); + } +} + +void setPointX(qreal arg) +{ + if (m_x != arg) { + m_x = arg; + emit pointXChanged(arg); + } +} + +void setPointY(qreal arg) +{ + if (m_y != arg) { + m_y = arg; + emit pointYChanged(arg); + } +} +void setAffectedParameter(AffectableParameters arg) +{ + if (m_physics != arg) { + m_physics = arg; + emit affectedParameterChanged(arg); + } +} + +void setProportionalToDistance(Proportion arg) +{ + if (m_proportionalToDistance != arg) { + m_proportionalToDistance = arg; + emit proportionalToDistanceChanged(arg); + } +} + +protected: + virtual bool affectParticle(QQuickParticleData *d, qreal dt); +private: +qreal m_strength; +qreal m_x; +qreal m_y; +AffectableParameters m_physics; +Proportion m_proportionalToDistance; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // ATTRACTORAFFECTOR_H diff --git a/src/particles/qquickpointdirection.cpp b/src/particles/qquickpointdirection.cpp new file mode 100644 index 0000000000..e35eb4854c --- /dev/null +++ b/src/particles/qquickpointdirection.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickpointdirection_p.h" +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PointDirection QQuickPointDirection + \inqmlmodule QtQuick.Particles 2 + \inherits Direction + \brief The PointDirection element allows you to specify a direction that varies in x and y components + + The PointDirection element allows both the specification of a direction by x and y components, + as well as varying the parameters by x or y component. +*/ +/*! + \qmlproperty real QtQuick.Particles2::PointDirection::x +*/ +/*! + \qmlproperty real QtQuick.Particles2::PointDirection::y +*/ +/*! + \qmlproperty real QtQuick.Particles2::PointDirection::xVariation +*/ +/*! + \qmlproperty real QtQuick.Particles2::PointDirection::yVariation +*/ + +QQuickPointDirection::QQuickPointDirection(QObject *parent) : + QQuickDirection(parent) + , m_x(0) + , m_y(0) + , m_xVariation(0) + , m_yVariation(0) +{ +} + +const QPointF QQuickPointDirection::sample(const QPointF &) +{ + QPointF ret; + ret.setX(m_x - m_xVariation + rand() / float(RAND_MAX) * m_xVariation * 2); + ret.setY(m_y - m_yVariation + rand() / float(RAND_MAX) * m_yVariation * 2); + return ret; +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickpointdirection_p.h b/src/particles/qquickpointdirection_p.h new file mode 100644 index 0000000000..7b18e6d8d7 --- /dev/null +++ b/src/particles/qquickpointdirection_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 POINTVECTOR_H +#define POINTVECTOR_H +#include "qquickdirection_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickPointDirection : public QQuickDirection +{ + Q_OBJECT + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + Q_PROPERTY(qreal xVariation READ xVariation WRITE setXVariation NOTIFY xVariationChanged) + Q_PROPERTY(qreal yVariation READ yVariation WRITE setYVariation NOTIFY yVariationChanged) +public: + explicit QQuickPointDirection(QObject *parent = 0); + virtual const QPointF sample(const QPointF &from); + qreal x() const + { + return m_x; + } + + qreal y() const + { + return m_y; + } + + qreal xVariation() const + { + return m_xVariation; + } + + qreal yVariation() const + { + return m_yVariation; + } + +signals: + + void xChanged(qreal arg); + + void yChanged(qreal arg); + + void xVariationChanged(qreal arg); + + void yVariationChanged(qreal arg); + +public slots: + void setX(qreal arg) + { + if (m_x != arg) { + m_x = arg; + emit xChanged(arg); + } + } + + void setY(qreal arg) + { + if (m_y != arg) { + m_y = arg; + emit yChanged(arg); + } + } + + void setXVariation(qreal arg) + { + if (m_xVariation != arg) { + m_xVariation = arg; + emit xVariationChanged(arg); + } + } + + void setYVariation(qreal arg) + { + if (m_yVariation != arg) { + m_yVariation = arg; + emit yVariationChanged(arg); + } + } + +private: + + qreal m_x; + qreal m_y; + qreal m_xVariation; + qreal m_yVariation; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // POINTVECTOR_H diff --git a/src/particles/qquickrectangleextruder.cpp b/src/particles/qquickrectangleextruder.cpp new file mode 100644 index 0000000000..ad2207ca9a --- /dev/null +++ b/src/particles/qquickrectangleextruder.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickrectangleextruder_p.h" +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass RectangleShape QQuickRectangleExtruder + \inqmlmodule QtQuick.Particles 2 + \brief The RectangleShape element allows you to specify an area for affectors and emitter. + + Just a rectangle. +*/ + +QQuickRectangleExtruder::QQuickRectangleExtruder(QObject *parent) : + QQuickParticleExtruder(parent), m_fill(true) +{ +} + +QPointF QQuickRectangleExtruder::extrude(const QRectF &rect) +{ + if (m_fill) + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + int side = rand() % 4; + switch (side){//TODO: Doesn't this overlap the corners? + case 0: + return QPointF(rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + case 1: + return QPointF(rect.width() + rect.x(), + ((qreal)rand() / RAND_MAX) * rect.height() + rect.y()); + case 2: + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + rect.y()); + default: + return QPointF(((qreal)rand() / RAND_MAX) * rect.width() + rect.x(), + rect.height() + rect.y()); + } +} + +bool QQuickRectangleExtruder::contains(const QRectF &bounds, const QPointF &point) +{ + return bounds.contains(point); +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickrectangleextruder_p.h b/src/particles/qquickrectangleextruder_p.h new file mode 100644 index 0000000000..1d4f8cc439 --- /dev/null +++ b/src/particles/qquickrectangleextruder_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 RECTANGLEEXTRUDER_H +#define RECTANGLEEXTRUDER_H + +#include "qquickparticleextruder_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickRectangleExtruder : public QQuickParticleExtruder +{ + Q_OBJECT + Q_PROPERTY(bool fill READ fill WRITE setFill NOTIFY fillChanged) + +public: + explicit QQuickRectangleExtruder(QObject *parent = 0); + virtual QPointF extrude(const QRectF &); + virtual bool contains(const QRectF &bounds, const QPointF &point); + bool fill() const + { + return m_fill; + } + +signals: + + void fillChanged(bool arg); + +public slots: + + void setFill(bool arg) + { + if (m_fill != arg) { + m_fill = arg; + emit fillChanged(arg); + } + } +protected: + bool m_fill; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // RectangleEXTRUDER_H diff --git a/src/particles/qquickspritegoal.cpp b/src/particles/qquickspritegoal.cpp new file mode 100644 index 0000000000..95d913cafa --- /dev/null +++ b/src/particles/qquickspritegoal.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickspritegoal_p.h" +#include <private/qquickspriteengine_p.h> +#include <private/qquicksprite_p.h> +#include "qquickimageparticle_p.h" +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass SpriteGoal QQuickSpriteGoalAffector + \inqmlmodule QtQuick.Particles 2 + \inherits Affector + \brief The SpriteGoal Affector allows you to change the state of a sprite particle. + +*/ +/*! + \qmlproperty string QtQuick.Particles2::SpriteGoal::goalState + + The name of the Sprite which the affected particles should move to. + + Sprite states have defined durations and transitions between them, setting goalState + will cause it to disregard any path weightings (including 0) and head down the path + which will reach the goalState quickest. It will pass through intermediate states + on that path. +*/ +/*! + \qmlproperty bool QtQuick.Particles2::SpriteGoal::jump + + If true, affected sprites will jump directly to the goal state instead of taking the + the shortest valid path to get there. They will also not finish their current state, + but immediately move to the beginning of the goal state. + + Default is false. +*/ +/*! + \qmlproperty bool QtQuick.Particles2::SpriteGoal::systemStates + + deprecated, use GroupGoal instead +*/ + +QQuickSpriteGoalAffector::QQuickSpriteGoalAffector(QQuickItem *parent) : + QQuickParticleAffector(parent), + m_goalIdx(-1), + m_lastEngine(0), + m_jump(false), + m_systemStates(false), + m_notUsingEngine(false) +{ + m_ignoresTime = true; +} + +void QQuickSpriteGoalAffector::updateStateIndex(QQuickStochasticEngine* e) +{ + if (m_systemStates){ + m_goalIdx = m_system->groupIds[m_goalState]; + }else{ + m_lastEngine = e; + for (int i=0; i<e->stateCount(); i++){ + if (e->state(i)->name() == m_goalState){ + m_goalIdx = i; + return; + } + } + m_goalIdx = -1;//Can't find it + } +} + +void QQuickSpriteGoalAffector::setGoalState(QString arg) +{ + if (m_goalState != arg) { + m_goalState = arg; + emit goalStateChanged(arg); + if (m_goalState.isEmpty()) + m_goalIdx = -1; + else + m_goalIdx = -2; + } +} + +bool QQuickSpriteGoalAffector::affectParticle(QQuickParticleData *d, qreal dt) +{ + Q_UNUSED(dt); + QQuickStochasticEngine *engine = 0; + if (!m_systemStates){ + //TODO: Affect all engines + foreach (QQuickParticlePainter *p, m_system->groupData[d->group]->painters) + if (qobject_cast<QQuickImageParticle*>(p)) + engine = qobject_cast<QQuickImageParticle*>(p)->spriteEngine(); + }else{ + engine = m_system->stateEngine; + if (!engine) + m_notUsingEngine = true; + } + if (!engine && !m_notUsingEngine) + return false; + + if (m_goalIdx == -2 || engine != m_lastEngine) + updateStateIndex(engine); + int index = d->index; + if (m_systemStates) + index = d->systemIndex; + if (m_notUsingEngine){//systemStates && no stochastic states defined. So cut out the engine + //TODO: It's possible to move to a group that is intermediate and not used by painters or emitters - but right now that will redirect to the default group + m_system->moveGroups(d, m_goalIdx); + }else if (engine->curState(index) != m_goalIdx){ + engine->setGoal(m_goalIdx, index, m_jump); + return true; //Doesn't affect particle data, but necessary for onceOff + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickspritegoal_p.h b/src/particles/qquickspritegoal_p.h new file mode 100644 index 0000000000..2b6b4f28fc --- /dev/null +++ b/src/particles/qquickspritegoal_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 SPRITEGOALAFFECTOR_H +#define SPRITEGOALAFFECTOR_H +#include "qquickparticleaffector_p.h" +#include <QtQml/qqmlinfo.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickStochasticEngine; + +class QQuickSpriteGoalAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(QString goalState READ goalState WRITE setGoalState NOTIFY goalStateChanged) + Q_PROPERTY(bool jump READ jump WRITE setJump NOTIFY jumpChanged) + Q_PROPERTY(bool systemStates READ systemStates WRITE setSystemStates NOTIFY systemStatesChanged) +public: + explicit QQuickSpriteGoalAffector(QQuickItem *parent = 0); + + QString goalState() const + { + return m_goalState; + } + + bool jump() const + { + return m_jump; + } + bool systemStates() const + { + return m_systemStates; + } + +protected: + virtual bool affectParticle(QQuickParticleData *d, qreal dt); +signals: + + void goalStateChanged(QString arg); + + void jumpChanged(bool arg); + + void systemStatesChanged(bool arg); + +public slots: + +void setGoalState(QString arg); + +void setJump(bool arg) +{ + if (m_jump != arg) { + m_jump = arg; + emit jumpChanged(arg); + } +} + +void setSystemStates(bool arg) +{ + if (m_systemStates != arg) { + //TODO: GroupGoal was added (and this deprecated) Oct 4 - remove it in a few weeks. + qmlInfo(this) << "systemStates is deprecated and will be removed soon. Use GroupGoal instead."; + m_systemStates = arg; + emit systemStatesChanged(arg); + } +} + +private: + void updateStateIndex(QQuickStochasticEngine* e); + QString m_goalState; + int m_goalIdx; + QQuickStochasticEngine* m_lastEngine; + bool m_jump; + bool m_systemStates; + + bool m_notUsingEngine; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SPRITEGOALAFFECTOR_H diff --git a/src/particles/qquicktargetdirection.cpp b/src/particles/qquicktargetdirection.cpp new file mode 100644 index 0000000000..695684dfde --- /dev/null +++ b/src/particles/qquicktargetdirection.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquicktargetdirection_p.h" +#include "qquickparticleemitter_p.h" +#include <cmath> +#include <QDebug> + +QT_BEGIN_NAMESPACE +/*! + \qmlclass TargetDirection QQuickTargetDirection + \inqmlmodule QtQuick.Particles 2 + \inherits Direction + \brief The TargetDirection element allows you to specify a direction towards the target point + +*/ +/*! + \qmlproperty real QtQuick.Particles2::TargetDirection::targetX +*/ +/*! + \qmlproperty real QtQuick.Particles2::TargetDirection::targetY +*/ +/*! + \qmlproperty Item QtQuick.Particles2::TargetDirection::targetItem + If specified, this will take precedence over targetX and targetY. + The targeted point will be the center of the specified Item +*/ +/*! + \qmlproperty real QtQuick.Particles2::TargetDirection::targetVariation +*/ +/*! + \qmlproperty real QtQuick.Particles2::TargetDirection::magnitude +*/ +/*! + \qmlproperty real QtQuick.Particles2::TargetDirection::magnitudeVariation +*/ +/*! + \qmlproperty bool QtQuick.Particles2::TargetDirection::proportionalMagnitude + + If true, then the value of magnitude and magnitudeVariation shall be interpreted as multiples + of the distance between the source point and the target point, per second. + + If false(default), then the value of magnitude and magnitudeVariation shall be interpreted as + pixels per second. +*/ + +QQuickTargetDirection::QQuickTargetDirection(QObject *parent) : + QQuickDirection(parent) + , m_targetX(0) + , m_targetY(0) + , m_targetVariation(0) + , m_proportionalMagnitude(false) + , m_magnitude(0) + , m_magnitudeVariation(0) + , m_targetItem(0) +{ +} + +const QPointF QQuickTargetDirection::sample(const QPointF &from) +{ + //###This approach loses interpolating the last position of the target (like we could with the emitter) is it worthwhile? + QPointF ret; + qreal targetX; + qreal targetY; + if (m_targetItem){ + QQuickParticleEmitter* parentEmitter = qobject_cast<QQuickParticleEmitter*>(parent()); + targetX = m_targetItem->width()/2; + targetY = m_targetItem->height()/2; + if (!parentEmitter){ + qWarning() << "Directed vector is not a child of the emitter. Mapping of target item coordinates may fail."; + targetX += m_targetItem->x(); + targetY += m_targetItem->y(); + }else{ + ret = parentEmitter->mapFromItem(m_targetItem, QPointF(targetX, targetY)); + targetX = ret.x(); + targetY = ret.y(); + } + }else{ + targetX = m_targetX; + targetY = m_targetY; + } + targetX += 0 - from.x() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2; + targetY += 0 - from.y() - m_targetVariation + rand()/(float)RAND_MAX * m_targetVariation*2; + qreal theta = atan2(targetY, targetX); + qreal mag = m_magnitude + rand()/(float)RAND_MAX * m_magnitudeVariation * 2 - m_magnitudeVariation; + if (m_proportionalMagnitude) + mag *= sqrt(targetX * targetX + targetY * targetY); + ret.setX(mag * cos(theta)); + ret.setY(mag * sin(theta)); + return ret; +} + +QT_END_NAMESPACE diff --git a/src/particles/qquicktargetdirection_p.h b/src/particles/qquicktargetdirection_p.h new file mode 100644 index 0000000000..0e0e942ca8 --- /dev/null +++ b/src/particles/qquicktargetdirection_p.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 DIRECTEDVECTOR_H +#define DIRECTEDVECTOR_H +#include "qquickdirection_p.h" +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickItem; +class QQuickTargetDirection : public QQuickDirection +{ + Q_OBJECT + Q_PROPERTY(qreal targetX READ targetX WRITE setTargetX NOTIFY targetXChanged) + Q_PROPERTY(qreal targetY READ targetY WRITE setTargetY NOTIFY targetYChanged) + //If targetItem is set, X/Y are ignored. Aims at middle of item, use variation for variation + Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged) + + Q_PROPERTY(qreal targetVariation READ targetVariation WRITE setTargetVariation NOTIFY targetVariationChanged) + + //TODO: An enum would be better + Q_PROPERTY(bool proportionalMagnitude READ proportionalMagnitude WRITE setProportionalMagnitude NOTIFY proprotionalMagnitudeChanged) + Q_PROPERTY(qreal magnitude READ magnitude WRITE setMagnitude NOTIFY magnitudeChanged) + Q_PROPERTY(qreal magnitudeVariation READ magnitudeVariation WRITE setMagnitudeVariation NOTIFY magnitudeVariationChanged) + +public: + explicit QQuickTargetDirection(QObject *parent = 0); + virtual const QPointF sample(const QPointF &from); + + qreal targetX() const + { + return m_targetX; + } + + qreal targetY() const + { + return m_targetY; + } + + qreal targetVariation() const + { + return m_targetVariation; + } + + qreal magnitude() const + { + return m_magnitude; + } + + bool proportionalMagnitude() const + { + return m_proportionalMagnitude; + } + + qreal magnitudeVariation() const + { + return m_magnitudeVariation; + } + + QQuickItem* targetItem() const + { + return m_targetItem; + } + +signals: + + void targetXChanged(qreal arg); + + void targetYChanged(qreal arg); + + void targetVariationChanged(qreal arg); + + void magnitudeChanged(qreal arg); + + void proprotionalMagnitudeChanged(bool arg); + + void magnitudeVariationChanged(qreal arg); + + void targetItemChanged(QQuickItem* arg); + +public slots: + void setTargetX(qreal arg) + { + if (m_targetX != arg) { + m_targetX = arg; + emit targetXChanged(arg); + } + } + + void setTargetY(qreal arg) + { + if (m_targetY != arg) { + m_targetY = arg; + emit targetYChanged(arg); + } + } + + void setTargetVariation(qreal arg) + { + if (m_targetVariation != arg) { + m_targetVariation = arg; + emit targetVariationChanged(arg); + } + } + + void setMagnitude(qreal arg) + { + if (m_magnitude != arg) { + m_magnitude = arg; + emit magnitudeChanged(arg); + } + } + + void setProportionalMagnitude(bool arg) + { + if (m_proportionalMagnitude != arg) { + m_proportionalMagnitude = arg; + emit proprotionalMagnitudeChanged(arg); + } + } + + void setMagnitudeVariation(qreal arg) + { + if (m_magnitudeVariation != arg) { + m_magnitudeVariation = arg; + emit magnitudeVariationChanged(arg); + } + } + + void setTargetItem(QQuickItem* arg) + { + if (m_targetItem != arg) { + m_targetItem = arg; + emit targetItemChanged(arg); + } + } + +private: + qreal m_targetX; + qreal m_targetY; + qreal m_targetVariation; + bool m_proportionalMagnitude; + qreal m_magnitude; + qreal m_magnitudeVariation; + QQuickItem *m_targetItem; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // DIRECTEDVECTOR_H diff --git a/src/particles/qquicktrailemitter.cpp b/src/particles/qquicktrailemitter.cpp new file mode 100644 index 0000000000..32f8763599 --- /dev/null +++ b/src/particles/qquicktrailemitter.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquicktrailemitter_p.h" +#include <private/qqmlengine_p.h> +#include <cmath> +QT_BEGIN_NAMESPACE + +/*! + \qmlclass TrailEmitter QQuickTrailEmitter + \inqmlmodule QtQuick.Particles 2 + \inherits QQuickParticleEmitter + \brief The TrailEmitter element allows you to emit logical particles from other logical particles. + + This element emits logical particles into the ParticleSystem, with the + starting positions based on those of other logical particles. +*/ +QQuickTrailEmitter::QQuickTrailEmitter(QQuickItem *parent) : + QQuickParticleEmitter(parent) + , m_particlesPerParticlePerSecond(0) + , m_lastTimeStamp(0) + , m_emitterXVariation(0) + , m_emitterYVariation(0) + , m_followCount(0) + , m_emissionExtruder(0) + , m_defaultEmissionExtruder(new QQuickParticleExtruder(this)) +{ + //TODO: If followed increased their size + connect(this, SIGNAL(followChanged(QString)), + this, SLOT(recalcParticlesPerSecond())); + connect(this, SIGNAL(particleDurationChanged(int)), + this, SLOT(recalcParticlesPerSecond())); + connect(this, SIGNAL(particlesPerParticlePerSecondChanged(int)), + this, SLOT(recalcParticlesPerSecond())); +} + +/*! + \qmlproperty string QtQuick.Particles2::TrailEmitter::follow + + The type of logical particle which this is emitting from. +*/ +/*! + \qmlproperty qreal QtQuick.Particles2::TrailEmitter::speedFromMovement + + If this value is non-zero, then any movement of the emitter will provide additional + starting velocity to the particles based on the movement. The additional vector will be the + same angle as the emitter's movement, with a magnitude that is the magnitude of the emitters + movement multiplied by speedFromMovement. + + Default value is 0. +*/ +/*! + \qmlproperty Shape QtQuick.Particles2::TrailEmitter::emitShape + + As the area of a TrailEmitter is the area it follows, a separate shape can be provided + to be the shape it emits out of. This shape has width and height specified by emitWidth + and emitHeight, and is centered on the followed particle's position. + + The default shape is a filled Rectangle. +*/ +/*! + \qmlproperty real QtQuick.Particles2::TrailEmitter::emitWidth + + The width in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize, + the width will be the current size of the particle being followed. + + Default is 0. +*/ +/*! + \qmlproperty real QtQuick.Particles2::TrailEmitter::emitHeight + + The height in pixels the emitShape is scaled to. If set to TrailEmitter.ParticleSize, + the height will be the current size of the particle being followed. + + Default is 0. +*/ +/*! + \qmlproperty real QtQuick.Particles2::TrailEmitter::emitRatePerParticle +*/ +/*! + \qmlsignal QtQuick.Particles2::TrailEmitter::emitFollowParticles(Array particles, real followed) + + This handler is called when particles are emitted from the \a followed particle. \a particles contains an array of particle objects which can be directly manipulated. + + If you use this signal handler, emitParticles will not be emitted. + +*/ + +bool QQuickTrailEmitter::isEmitFollowConnected() +{ + static int idx = QObjectPrivate::get(this)->signalIndex("emitFollowParticles(QQmlV8Handle,QQmlV8Handle)"); + return QObjectPrivate::get(this)->isSignalConnected(idx); +} + +void QQuickTrailEmitter::recalcParticlesPerSecond(){ + if (!m_system) + return; + m_followCount = m_system->groupData[m_system->groupIds[m_follow]]->size(); + if (!m_followCount){ + setParticlesPerSecond(1);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS) + }else{ + setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount); + m_lastEmission.resize(m_followCount); + m_lastEmission.fill(m_lastTimeStamp); + } +} + +void QQuickTrailEmitter::reset() +{ + m_followCount = 0; +} + +void QQuickTrailEmitter::emitWindow(int timeStamp) +{ + if (m_system == 0) + return; + if (!m_enabled && !m_pulseLeft && m_burstQueue.isEmpty()) + return; + if (m_followCount != m_system->groupData[m_system->groupIds[m_follow]]->size()){ + qreal oldPPS = m_particlesPerSecond; + recalcParticlesPerSecond(); + if (m_particlesPerSecond != oldPPS) + return;//system may need to update + } + + if (m_pulseLeft){ + m_pulseLeft -= timeStamp - m_lastTimeStamp * 1000.; + if (m_pulseLeft < 0){ + timeStamp += m_pulseLeft; + m_pulseLeft = 0; + } + } + + //TODO: Implement startTime and speedFromMovement + qreal time = timeStamp / 1000.; + qreal particleRatio = 1. / m_particlesPerParticlePerSecond; + qreal pt; + qreal maxLife = (m_particleDuration + m_particleDurationVariation)/1000.0; + + //Have to map it into this system, because particlesystem automaps it back + QPointF offset = m_system->mapFromItem(this, QPointF(0, 0)); + qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize; + + int gId = m_system->groupIds[m_follow]; + int gId2 = m_system->groupIds[m_group]; + for (int i=0; i<m_system->groupData[gId]->data.count(); i++) { + QQuickParticleData *d = m_system->groupData[gId]->data[i]; + if (!d->stillAlive()){ + m_lastEmission[i] = time; //Should only start emitting when it returns to life + continue; + } + pt = m_lastEmission[i]; + if (pt < d->t) + pt = d->t; + if (pt + maxLife < time)//We missed so much, that we should skip emiting particles that are dead by now + pt = time - maxLife; + + if ((width() || height()) && !effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){ + m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside + continue; + } + + QList<QQuickParticleData*> toEmit; + + while (pt < time || !m_burstQueue.isEmpty()){ + QQuickParticleData* datum = m_system->newDatum(gId2, !m_overwrite); + if (datum){//else, skip this emission + datum->e = this;//###useful? + + // Particle timestamp + datum->t = pt; + datum->lifeSpan = + (m_particleDuration + + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation)) + / 1000.0; + + // Particle position + // Note that burst location doesn't get used for follow emitter + qreal followT = pt - d->t; + qreal followT2 = followT * followT * 0.5; + qreal eW = m_emitterXVariation < 0 ? d->curSize() : m_emitterXVariation; + qreal eH = m_emitterYVariation < 0 ? d->curSize() : m_emitterYVariation; + //Subtract offset, because PS expects this in emitter coordinates + QRectF boundsRect(d->x - offset.x() + d->vx * followT + d->ax * followT2 - eW/2, + d->y - offset.y() + d->vy * followT + d->ay * followT2 - eH/2, + eW, eH); + + QQuickParticleExtruder* effectiveEmissionExtruder = m_emissionExtruder ? m_emissionExtruder : m_defaultEmissionExtruder; + const QPointF &newPos = effectiveEmissionExtruder->extrude(boundsRect); + datum->x = newPos.x(); + datum->y = newPos.y(); + + // Particle speed + const QPointF &speed = m_speed->sample(newPos); + datum->vx = speed.x() + + m_speed_from_movement * d->vx; + datum->vy = speed.y() + + m_speed_from_movement * d->vy; + + // 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 * float(m_enabled); + datum->endSize = endSize * float(m_enabled); + + toEmit << datum; + + m_system->emitParticle(datum); + } + if (!m_burstQueue.isEmpty()){ + m_burstQueue.first().first--; + if (m_burstQueue.first().first <= 0) + m_burstQueue.pop_front(); + }else{ + pt += particleRatio; + } + } + + if (isEmitConnected() || isEmitFollowConnected()) { + v8::HandleScope handle_scope; + v8::Context::Scope scope(QQmlEnginePrivate::getV8Engine(qmlEngine(this))->context()); + v8::Handle<v8::Array> array = v8::Array::New(toEmit.size()); + for (int i=0; i<toEmit.size(); i++) + array->Set(i, toEmit[i]->v8Value().toHandle()); + + if (isEmitFollowConnected()) + emitFollowParticles(QQmlV8Handle::fromHandle(array), d->v8Value());//A chance for many arbitrary JS changes + else if (isEmitConnected()) + emitParticles(QQmlV8Handle::fromHandle(array));//A chance for arbitrary JS changes + } + foreach (QQuickParticleData* d, toEmit) + m_system->emitParticle(d); + m_lastEmission[d->index] = pt; + } + + m_lastTimeStamp = time; +} +QT_END_NAMESPACE diff --git a/src/particles/qquicktrailemitter_p.h b/src/particles/qquicktrailemitter_p.h new file mode 100644 index 0000000000..0b63a444ec --- /dev/null +++ b/src/particles/qquicktrailemitter_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 FOLLOWEMITTER_H +#define FOLLOWEMITTER_H +#include "qquickparticleemitter_p.h" +#include "qquickparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickTrailEmitter : public QQuickParticleEmitter +{ + Q_OBJECT + Q_PROPERTY(QString follow READ follow WRITE setFollow NOTIFY followChanged) + Q_PROPERTY(int emitRatePerParticle READ particlesPerParticlePerSecond WRITE setParticlesPerParticlePerSecond NOTIFY particlesPerParticlePerSecondChanged) + + Q_PROPERTY(QQuickParticleExtruder* emitShape READ emissonShape WRITE setEmissionShape NOTIFY emissionShapeChanged) + Q_PROPERTY(qreal emitHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged) + Q_PROPERTY(qreal emitWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged) + + Q_ENUMS(EmitSize) +public: + enum EmitSize { + ParticleSize = -2//Anything less than 0 will do + }; + explicit QQuickTrailEmitter(QQuickItem *parent = 0); + virtual void emitWindow(int timeStamp); + virtual void reset(); + + int particlesPerParticlePerSecond() const + { + return m_particlesPerParticlePerSecond; + } + + qreal emitterXVariation() const + { + return m_emitterXVariation; + } + + qreal emitterYVariation() const + { + return m_emitterYVariation; + } + + QString follow() const + { + return m_follow; + } + + QQuickParticleExtruder* emissonShape() const + { + return m_emissionExtruder; + } + +signals: + void emitFollowParticles(QQmlV8Handle particles, QQmlV8Handle followed); + + void particlesPerParticlePerSecondChanged(int arg); + + void emitterXVariationChanged(qreal arg); + + void emitterYVariationChanged(qreal arg); + + void followChanged(QString arg); + + void emissionShapeChanged(QQuickParticleExtruder* arg); + +public slots: + + void setParticlesPerParticlePerSecond(int arg) + { + if (m_particlesPerParticlePerSecond != arg) { + m_particlesPerParticlePerSecond = arg; + emit particlesPerParticlePerSecondChanged(arg); + } + } + void setEmitterXVariation(qreal arg) + { + if (m_emitterXVariation != arg) { + m_emitterXVariation = arg; + emit emitterXVariationChanged(arg); + } + } + + void setEmitterYVariation(qreal arg) + { + if (m_emitterYVariation != arg) { + m_emitterYVariation = arg; + emit emitterYVariationChanged(arg); + } + } + + void setFollow(QString arg) + { + if (m_follow != arg) { + m_follow = arg; + emit followChanged(arg); + } + } + + void setEmissionShape(QQuickParticleExtruder* arg) + { + if (m_emissionExtruder != arg) { + m_emissionExtruder = arg; + emit emissionShapeChanged(arg); + } + } + +private slots: + void recalcParticlesPerSecond(); + +private: + QSet<QQuickParticleData*> m_pending; + QVector<qreal> m_lastEmission; + int m_particlesPerParticlePerSecond; + qreal m_lastTimeStamp; + qreal m_emitterXVariation; + qreal m_emitterYVariation; + QString m_follow; + int m_followCount; + QQuickParticleExtruder* m_emissionExtruder; + QQuickParticleExtruder* m_defaultEmissionExtruder; + bool isEmitFollowConnected(); +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // FOLLOWEMITTER_H diff --git a/src/particles/qquickturbulence.cpp b/src/particles/qquickturbulence.cpp new file mode 100644 index 0000000000..18ecc6a4cd --- /dev/null +++ b/src/particles/qquickturbulence.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickturbulence_p.h" +#include "qquickparticlepainter_p.h"//TODO: Why was this needed again? +#include <cmath> +#include <cstdlib> +#include <QDebug> +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Turbulence QQuickTurbulenceAffector + \inqmlmodule QtQuick.Particles 2 + \inherits Affector + \brief Turbulence provides fluid like forces based on a noise image. + + The Turbulence Element scales the noise source over the area it affects, + and uses the curl of that source to generate force vectors. + + Turbulence requires a fixed size. Unlike other affectors, a 0x0 Turbulence element + will affect no particles. + + The source should be relatively smooth black and white noise, such as perlin noise. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Turbulence::strength + + The magnitude of the velocity vector at any point varies between zero and + the square root of two. It will then be multiplied by strength to get the + velocity per second for the particles affected by the turbulence. +*/ +/*! + \qmlproperty url QtQuick.Particles2::Turbulence::noiseSource + + The source image to generate the turbulence from. It will be scaled to the size of the element, + so equal or larger sizes will give better results. Tweaking this image is the only way to tweak + behavior such as where vortices are or how many exist. + + The source should be a relatively smooth black and white noise image, such as perlin noise. + A default image will be used if none is provided. +*/ + +QQuickTurbulenceAffector::QQuickTurbulenceAffector(QQuickItem *parent) : + QQuickParticleAffector(parent), + m_strength(10), m_lastT(0), m_gridSize(0), m_field(0), m_vectorField(0), m_inited(false) +{ +} + +void QQuickTurbulenceAffector::geometryChanged(const QRectF &, const QRectF &) +{ + initializeGrid(); +} + +QQuickTurbulenceAffector::~QQuickTurbulenceAffector() +{ + if (m_field) { + for (int i=0; i<m_gridSize; i++) + free(m_field[i]); + free(m_field); + } + if (m_vectorField) { + for (int i=0; i<m_gridSize; i++) + free(m_vectorField[i]); + free(m_vectorField); + } +} + +void QQuickTurbulenceAffector::initializeGrid() +{ + if (!m_inited) + return; + + int arg = qMax(width(), height()); + if (m_gridSize != arg) { + if (m_field){ //deallocate and then reallocate grid + for (int i=0; i<m_gridSize; i++) + free(m_field[i]); + free(m_field); + m_system = 0; + } + if (m_vectorField) { + for (int i=0; i<m_gridSize; i++) + free(m_vectorField[i]); + free(m_vectorField); + } + m_gridSize = arg; + } + + m_field = (qreal**)malloc(m_gridSize * sizeof(qreal*)); + for (int i=0; i<m_gridSize; i++) + m_field[i] = (qreal*)malloc(m_gridSize * sizeof(qreal)); + m_vectorField = (QPointF**)malloc(m_gridSize * sizeof(QPointF*)); + for (int i=0; i<m_gridSize; i++) + m_vectorField[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF)); + + QImage image; + if (!m_noiseSource.isEmpty()) + image = QImage(m_noiseSource.toLocalFile()).scaled(QSize(m_gridSize, m_gridSize)); + if (image.isNull()) + image = QImage(QStringLiteral(":particleresources/noise.png")).scaled(QSize(m_gridSize, m_gridSize)); + + for (int i=0; i<m_gridSize; i++) + for (int j=0; j<m_gridSize; j++) + m_field[i][j] = qRed(image.pixel(QPoint(i,j)));//Red as proxy for Value + for (int i=0; i<m_gridSize; i++){ + for (int j=0; j<m_gridSize; j++){ + m_vectorField[i][j].setX(boundsRespectingField(i,j) - boundsRespectingField(i,j-1)); + m_vectorField[i][j].setY(boundsRespectingField(i-1,j) - boundsRespectingField(i,j)); + } + } +} + +qreal QQuickTurbulenceAffector::boundsRespectingField(int x, int y) +{ + if (x < 0) + x = 0; + if (x >= m_gridSize) + x = m_gridSize - 1; + if (y < 0) + y = 0; + if (y >= m_gridSize) + y = m_gridSize - 1; + return m_field[x][y]; +} + +void QQuickTurbulenceAffector::ensureInit() +{ + if (m_inited) + return; + m_inited = true; + initializeGrid(); +} + +void QQuickTurbulenceAffector::affectSystem(qreal dt) +{ + if (!m_system || !m_enabled) + return; + ensureInit(); + if (!m_gridSize) + return; + + updateOffsets();//### Needed if an ancestor is transformed. + + QRect boundsRect(0,0,m_gridSize,m_gridSize); + foreach (QQuickParticleGroupData *gd, m_system->groupData){ + if (!activeGroup(m_system->groupData.key(gd))) + continue; + foreach (QQuickParticleData *d, gd->data){ + if (!shouldAffect(d)) + continue; + QPoint pos = (QPointF(d->curX(), d->curY()) - m_offset).toPoint(); + if (!boundsRect.contains(pos,true))//Need to redo bounds checking due to quantization. + continue; + qreal fx = 0.0; + qreal fy = 0.0; + fx += m_vectorField[pos.x()][pos.y()].x() * m_strength; + fy += m_vectorField[pos.x()][pos.y()].y() * m_strength; + if (fx || fy){ + d->setInstantaneousVX(d->curVX()+ fx * dt); + d->setInstantaneousVY(d->curVY()+ fy * dt); + postAffect(d); + } + } + } +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickturbulence_p.h b/src/particles/qquickturbulence_p.h new file mode 100644 index 0000000000..cd5535c387 --- /dev/null +++ b/src/particles/qquickturbulence_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 TURBULENCEAFFECTOR_H +#define TURBULENCEAFFECTOR_H +#include "qquickparticleaffector_p.h" +#include <QQmlListProperty> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickParticlePainter; + +class QQuickTurbulenceAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) + Q_PROPERTY(QUrl noiseSource READ noiseSource WRITE setNoiseSource NOTIFY noiseSourceChanged) + public: + explicit QQuickTurbulenceAffector(QQuickItem *parent = 0); + ~QQuickTurbulenceAffector(); + virtual void affectSystem(qreal dt); + + qreal strength() const + { + return m_strength; + } + + QUrl noiseSource() const + { + return m_noiseSource; + } +signals: + + void strengthChanged(qreal arg); + + void noiseSourceChanged(QUrl arg); + +public slots: + + void setStrength(qreal arg) + { + if (m_strength != arg) { + m_strength = arg; + emit strengthChanged(arg); + } + } + + void setNoiseSource(QUrl arg) + { + if (m_noiseSource != arg) { + m_noiseSource = arg; + emit noiseSourceChanged(arg); + initializeGrid(); + } + } + +protected: + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); +private: + void ensureInit(); + void mapUpdate(); + void initializeGrid(); + qreal boundsRespectingField(int x, int y); + qreal m_strength; + qreal m_lastT; + int m_gridSize; + qreal** m_field; + QPointF** m_vectorField; + bool m_inited; + QUrl m_noiseSource; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // TURBULENCEAFFECTOR_H diff --git a/src/particles/qquickv8particledata.cpp b/src/particles/qquickv8particledata.cpp new file mode 100644 index 0000000000..8e50e41091 --- /dev/null +++ b/src/particles/qquickv8particledata.cpp @@ -0,0 +1,505 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickv8particledata_p.h" +#include "qquickparticlesystem_p.h"//for QQuickParticleData +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Particle + \inqmlmodule QtQuick.Particles 2 + \brief Particle elements can be manipulated in custom emitters and affectors. + + Particle elements are always managed internally by the ParticleSystem and cannot be created in QML. + However, sometimes they are exposed via signals so as to allow arbitrary changes to the particle state +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::initialX + The x coordinate of the particle at the beginning of its lifetime. + + The method of simulation prefers to have the initial values changed, rather + than determining and changing the value at a given time. Change initial + values in CustomEmitters instead of the current values. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::initialVX + The x velocity of the particle at the beginning of its lifetime. + + The method of simulation prefers to have the initial values changed, rather + than determining and changing the value at a given time. Change initial + values in CustomEmitters instead of the current values. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::initialAX + The x acceleration of the particle at the beginning of its lifetime. + + The method of simulation prefers to have the initial values changed, rather + than determining and changing the value at a given time. Change initial + values in CustomEmitters instead of the current values. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::initialY + The y coordinate of the particle at the beginning of its lifetime. + + The method of simulation prefers to have the initial values changed, rather + than determining and changing the value at a given time. Change initial + values in CustomEmitters instead of the current values. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::initialVY + The y velocity of the particle at the beginning of its lifetime. + + The method of simulation prefers to have the initial values changed, rather + than determining and changing the value at a given time. Change initial + values in CustomEmitters instead of the current values. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::initialAY + The y acceleration of the particle at the beginning of its lifetime. + + The method of simulation prefers to have the initial values changed, rather + than determining and changing the value at a given time. Change initial + values in CustomEmitters instead of the current values. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::x + The current x coordinate of the particle. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::vx + The current x velocity of the particle. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::ax + The current x acceleration of the particle. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::y + The current y coordinate of the particle. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::vy + The current y velocity of the particle. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::ay + The current y acceleration of the particle. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::t + The time, in seconds since the beginning of the simulation, that the particle was born. +*/ + + +/*! + \qmlproperty real QtQuick.Particles2::Particle::startSize + The size in pixels that the particle image is at the start + of its life. +*/ + + +/*! + \qmlproperty real QtQuick.Particles2::Particle::endSize + The size in pixels that the particle image is at the end + of its life. If this value is less than 0, then it is + disregarded and the particle will have its startSize for the + entire lifetime. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::lifeSpan + The time in seconds that the particle will live for. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::rotation + Degrees clockwise that the particle image is rotated at + the beginning of its life. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::rotationSpeed + Degrees clockwise per second that the particle image is rotated at while alive. +*/ +/*! + \qmlproperty bool QtQuick.Particles2::Particle::autoRotate + If autoRotate is true, then the particle's rotation will be + set so that it faces the direction of travel, plus any + rotation from the rotation or rotationSpeed properties. +*/ + +/*! + \qmlproperty bool QtQuick.Particles2::Particle::update + + Inside an Affector, the changes made to the particle will only be + applied if update is set to true. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Particle::xDeformationVectorX + + The x component of the deformation vector along the X axis. ImageParticle + can draw particles across non-square shapes. It will draw the texture rectangle + across the parallelogram drawn with the x and y deformation vectors. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::yDeformationVectorX + + The y component of the deformation vector along the X axis. ImageParticle + can draw particles across non-square shapes. It will draw the texture rectangle + across the parallelogram drawn with the x and y deformation vectors. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::xDeformationVectorY + + The x component of the deformation vector along the X axis. ImageParticle + can draw particles across non-square shapes. It will draw the texture rectangle + across the parallelogram drawn with the x and y deformation vectors. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::yDeformationVectorY + + The y component of the deformation vector along the Y axis. ImageParticle + can draw particles across non-square shapes. It will draw the texture rectangle + across the parallelogram drawn with the x and y deformation vectors. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::red + + ImageParticle can draw colorized particles. When it does so, red is used + as the red channel of the color applied to the source image. + + Values are from 0.0 to 1.0. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::green + + ImageParticle can draw colorized particles. When it does so, green is used + as the green channel of the color applied to the source image. + + Values are from 0.0 to 1.0. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::blue + + ImageParticle can draw colorized particles. When it does so, blue is used + as the blue channel of the color applied to the source image. + + Values are from 0.0 to 1.0. +*/ + +/*! + \qmlproperty real QtQuick.Particles2::Particle::alpha + + ImageParticle can draw colorized particles. When it does so, alpha is used + as the alpha channel of the color applied to the source image. + + Values are from 0.0 to 1.0. +*/ +/*! + \qmlmethod real QtQuick.Particles2::Particle::lifeLeft + The time in seconds that the particle has left to live at + the current point in time. +*/ +/*! + \qmlmethod real QtQuick.Particles2::Particle::currentSize + The currentSize of the particle, interpolating between startSize and endSize based on the currentTime. +*/ + + + +//### Particle data handles are not locked to within certain scopes like QQuickContext2D, but there's no way to reload either... +class QV8ParticleDataResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ParticleDataType) +public: + QV8ParticleDataResource(QV8Engine *e) : QV8ObjectResource(e) {} + QQuickParticleData* 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 COLOR_GETTER_AND_SETTER(VAR, NAME) static v8::Handle<v8::Value> particleData_get_ ## NAME (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->color. VAR )/255.0);\ +}\ +\ +static void particleData_set_ ## NAME (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->color. VAR = qMin(255, qMax(0, (int)floor(value->NumberValue() * 255.0)));\ +} + + +#define SEMIBOOL_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::Boolean::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->BooleanValue() ? 1.0 : 0.0;\ +} + +#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 FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) 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-> GETTER ());\ +}\ +\ +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-> SETTER ( value->NumberValue() );\ +} + +#define REGISTER_ACCESSOR(FT, ENGINE, VARIABLE, NAME) FT ->PrototypeTemplate()->SetAccessor( v8::String::New( #NAME ), particleData_get_ ## VARIABLE , particleData_set_ ## VARIABLE , v8::External::Wrap(ENGINE)) + +COLOR_GETTER_AND_SETTER(r, red) +COLOR_GETTER_AND_SETTER(g, green) +COLOR_GETTER_AND_SETTER(b, blue) +COLOR_GETTER_AND_SETTER(a, alpha) +SEMIBOOL_GETTER_AND_SETTER(autoRotate) +SEMIBOOL_GETTER_AND_SETTER(update) +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(vx) +FLOAT_GETTER_AND_SETTER(vy) +FLOAT_GETTER_AND_SETTER(ax) +FLOAT_GETTER_AND_SETTER(ay) +FLOAT_GETTER_AND_SETTER(xx) +FLOAT_GETTER_AND_SETTER(xy) +FLOAT_GETTER_AND_SETTER(yx) +FLOAT_GETTER_AND_SETTER(yy) +FLOAT_GETTER_AND_SETTER(rotation) +FLOAT_GETTER_AND_SETTER(rotationSpeed) +FLOAT_GETTER_AND_SETTER(animIdx) +FLOAT_GETTER_AND_SETTER(frameDuration) +FLOAT_GETTER_AND_SETTER(frameAt) +FLOAT_GETTER_AND_SETTER(frameCount) +FLOAT_GETTER_AND_SETTER(animT) +FLOAT_GETTER_AND_SETTER(r) +FAKE_FLOAT_GETTER_AND_SETTER(curX, curX, setInstantaneousX) +FAKE_FLOAT_GETTER_AND_SETTER(curVX, curVX, setInstantaneousVX) +FAKE_FLOAT_GETTER_AND_SETTER(curAX, curAX, setInstantaneousAX) +FAKE_FLOAT_GETTER_AND_SETTER(curY, curY, setInstantaneousY) +FAKE_FLOAT_GETTER_AND_SETTER(curVY, curVY, setInstantaneousVY) +FAKE_FLOAT_GETTER_AND_SETTER(curAY, curAY, setInstantaneousAY) + +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("currentSize"), V8FUNCTION(particleData_curSize, engine)); + REGISTER_ACCESSOR(ft, engine, x, initialX); + REGISTER_ACCESSOR(ft, engine, y, initialY); + REGISTER_ACCESSOR(ft, engine, t, t); + REGISTER_ACCESSOR(ft, engine, lifeSpan, lifeSpan); + REGISTER_ACCESSOR(ft, engine, size, startSize); + REGISTER_ACCESSOR(ft, engine, endSize, endSize); + REGISTER_ACCESSOR(ft, engine, vx, initialVX); + REGISTER_ACCESSOR(ft, engine, vy, initialVY); + REGISTER_ACCESSOR(ft, engine, ax, initialAX); + REGISTER_ACCESSOR(ft, engine, ay, initialAY); + REGISTER_ACCESSOR(ft, engine, xx, xDeformationVectorX); + REGISTER_ACCESSOR(ft, engine, xy, xDeformationVectorY); + REGISTER_ACCESSOR(ft, engine, yx, yDeformationVectorX); + REGISTER_ACCESSOR(ft, engine, yy, yDeformationVectorY); + REGISTER_ACCESSOR(ft, engine, rotation, rotation); + REGISTER_ACCESSOR(ft, engine, rotationSpeed, rotationSpeed); + REGISTER_ACCESSOR(ft, engine, autoRotate, autoRotate); + REGISTER_ACCESSOR(ft, engine, animIdx, animationIndex); + REGISTER_ACCESSOR(ft, engine, frameDuration, frameDuration); + REGISTER_ACCESSOR(ft, engine, frameAt, frameAt); + REGISTER_ACCESSOR(ft, engine, frameCount, frameCount); + REGISTER_ACCESSOR(ft, engine, animT, animationT); + REGISTER_ACCESSOR(ft, engine, r, r); + REGISTER_ACCESSOR(ft, engine, update, update); + REGISTER_ACCESSOR(ft, engine, curX, x); + REGISTER_ACCESSOR(ft, engine, curVX, vx); + REGISTER_ACCESSOR(ft, engine, curAX, ax); + REGISTER_ACCESSOR(ft, engine, curY, y); + REGISTER_ACCESSOR(ft, engine, curVY, vy); + REGISTER_ACCESSOR(ft, engine, curAY, ay); + REGISTER_ACCESSOR(ft, engine, red, red); + REGISTER_ACCESSOR(ft, engine, green, green); + REGISTER_ACCESSOR(ft, engine, blue, blue); + REGISTER_ACCESSOR(ft, engine, alpha, alpha); + + constructor = qPersistentNew(ft->GetFunction()); +} + +QV8ParticleDataDeletable::~QV8ParticleDataDeletable() +{ + qPersistentDispose(constructor); +} + +V8_DEFINE_EXTENSION(QV8ParticleDataDeletable, particleV8Data); + + +QQuickV8ParticleData::QQuickV8ParticleData(QV8Engine* engine, QQuickParticleData* 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); +} + +QQuickV8ParticleData::~QQuickV8ParticleData() +{ + qPersistentDispose(m_v8Value); +} + +QQmlV8Handle QQuickV8ParticleData::v8Value() +{ + return QQmlV8Handle::fromHandle(m_v8Value); +} + +QT_END_NAMESPACE diff --git a/src/particles/qquickv8particledata_p.h b/src/particles/qquickv8particledata_p.h new file mode 100644 index 0000000000..21ec6458c1 --- /dev/null +++ b/src/particles/qquickv8particledata_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 QQuickV8PARTICLEDATA_H +#define QQuickV8PARTICLEDATA_H + +#include <private/qv8engine_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickParticleData; +class QQuickV8ParticleData { +public: + QQuickV8ParticleData(QV8Engine*,QQuickParticleData*); + ~QQuickV8ParticleData(); + QQmlV8Handle v8Value(); +private: + v8::Persistent<v8::Object> m_v8Value; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif diff --git a/src/particles/qquickwander.cpp b/src/particles/qquickwander.cpp new file mode 100644 index 0000000000..0f9a5f069b --- /dev/null +++ b/src/particles/qquickwander.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 "qquickwander_p.h" +#include "qquickparticlesystem_p.h"//for ParticlesVertices +QT_BEGIN_NAMESPACE +/*! + \qmlclass Wander QQuickWanderAffector + \inqmlmodule QtQuick.Particles 2 + \inherits Affector + \brief The Wander affector allows particles to randomly vary their trajectory. + +*/ +/*! + \qmlproperty real QtQuick.Particles2::Wander::pace + + Maximum attribute change per second. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Wander::xVariance + + Maximum attribute x value (as a result of Wander). + + If unset, Wander will not affect x values. +*/ +/*! + \qmlproperty real QtQuick.Particles2::Wander::yVariance + + Maximum attribute y value (as a result of Wander). + + If unset, Wander will not affect y values. +*/ +/*! + \qmlproperty AffectableParameter QtQuick.Particles2::Wander::affectedParameter + + What attribute of particles is directly affected. + \list + \li PointAttractor.Position + \li PointAttractor.Velocity + \li PointAttractor.Acceleration + \endlist +*/ + +QQuickWanderAffector::QQuickWanderAffector(QQuickItem *parent) : + QQuickParticleAffector(parent), m_xVariance(0), m_yVariance(0), m_pace(0) + , m_affectedParameter(Velocity) +{ + m_needsReset = true; +} + +QQuickWanderAffector::~QQuickWanderAffector() +{ + for (QHash<int, WanderData*>::const_iterator iter=m_wanderData.constBegin(); + iter != m_wanderData.constEnd(); iter++) + delete (*iter); +} + +WanderData* QQuickWanderAffector::getData(int idx) +{ + if (m_wanderData.contains(idx)) + return m_wanderData[idx]; + WanderData* d = new WanderData; + d->x_vel = 0; + d->y_vel = 0; + d->x_peak = m_xVariance; + d->y_peak = m_yVariance; + d->x_var = m_pace * qreal(qrand()) / RAND_MAX; + d->y_var = m_pace * qreal(qrand()) / RAND_MAX; + + m_wanderData.insert(idx, d); + return d; +} + +// TODO: see below +//void QQuickWanderAffector::reset(int systemIdx) +//{ +// if (m_wanderData.contains(systemIdx)) +// delete m_wanderData[systemIdx]; +// m_wanderData.remove(systemIdx); +//} + +bool QQuickWanderAffector::affectParticle(QQuickParticleData* data, qreal dt) +{ + /*TODO: Add a mode which does basically this - picking a direction, going in it (random speed) and then going back + WanderData* d = getData(data->systemIndex); + if (m_xVariance != 0.) { + if ((d->x_vel > d->x_peak && d->x_var > 0.0) || (d->x_vel < -d->x_peak && d->x_var < 0.0)) { + d->x_var = -d->x_var; + d->x_peak = m_xVariance + m_xVariance * qreal(qrand()) / RAND_MAX; + } + d->x_vel += d->x_var * dt; + } + qreal dx = dt * d->x_vel; + + if (m_yVariance != 0.) { + if ((d->y_vel > d->y_peak && d->y_var > 0.0) || (d->y_vel < -d->y_peak && d->y_var < 0.0)) { + d->y_var = -d->y_var; + d->y_peak = m_yVariance + m_yVariance * qreal(qrand()) / RAND_MAX; + } + d->y_vel += d->y_var * dt; + } + qreal dy = dt * d->x_vel; + + //### Should we be amending vel instead? + ParticleVertex* p = &(data->pv); + p->x += dx; + + p->y += dy; + return true; + */ + qreal dx = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1); + qreal dy = dt * m_pace * (2 * qreal(qrand())/RAND_MAX - 1); + qreal newX, newY; + switch (m_affectedParameter){ + case Position: + newX = data->curX() + dx; + if (m_xVariance > qAbs(newX) ) + data->x += dx; + newY = data->curY() + dy; + if (m_yVariance > qAbs(newY) ) + data->y += dy; + break; + default: + case Velocity: + newX = data->curVX() + dx; + if (m_xVariance > qAbs(newX) ) + data->setInstantaneousVX(newX); + newY = data->curVY() + dy; + if (m_yVariance > qAbs(newY) ) + data->setInstantaneousVY(newY); + break; + case Acceleration: + newX = data->ax + dx; + if (m_xVariance > qAbs(newX) ) + data->setInstantaneousAX(newX); + newY = data->ay + dy; + if (m_yVariance > qAbs(newY) ) + data->setInstantaneousAY(newY); + break; + } + return true; +} +QT_END_NAMESPACE diff --git a/src/particles/qquickwander_p.h b/src/particles/qquickwander_p.h new file mode 100644 index 0000000000..bb418f7912 --- /dev/null +++ b/src/particles/qquickwander_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuick 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 WANDERAFFECTOR_H +#define WANDERAFFECTOR_H +#include <QHash> +#include "qquickparticleaffector_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +struct WanderData{ + qreal x_vel; + qreal y_vel; + qreal x_peak; + qreal x_var; + qreal y_peak; + qreal y_var; +}; + +class QQuickWanderAffector : public QQuickParticleAffector +{ + Q_OBJECT + Q_PROPERTY(qreal pace READ pace WRITE setPace NOTIFY paceChanged) + Q_PROPERTY(qreal xVariance READ xVariance WRITE setXVariance NOTIFY xVarianceChanged) + Q_PROPERTY(qreal yVariance READ yVariance WRITE setYVariance NOTIFY yVarianceChanged) + Q_PROPERTY(AffectableParameters affectedParameter READ affectedParameter WRITE setAffectedParameter NOTIFY affectedParameterChanged) + Q_ENUMS(AffectableParameters) + +public: + enum AffectableParameters { + Position, + Velocity, + Acceleration + }; + + explicit QQuickWanderAffector(QQuickItem *parent = 0); + ~QQuickWanderAffector(); +// virtual void reset(int systemIdx); + + qreal xVariance() const + { + return m_xVariance; + } + + qreal yVariance() const + { + return m_yVariance; + } + + qreal pace() const + { + return m_pace; + } + + AffectableParameters affectedParameter() const + { + return m_affectedParameter; + } + +protected: + virtual bool affectParticle(QQuickParticleData *d, qreal dt); +signals: + + void xVarianceChanged(qreal arg); + + void yVarianceChanged(qreal arg); + + void paceChanged(qreal arg); + + + void affectedParameterChanged(AffectableParameters arg); + +public slots: +void setXVariance(qreal arg) +{ + if (m_xVariance != arg) { + m_xVariance = arg; + emit xVarianceChanged(arg); + } +} + +void setYVariance(qreal arg) +{ + if (m_yVariance != arg) { + m_yVariance = arg; + emit yVarianceChanged(arg); + } +} + +void setPace(qreal arg) +{ + if (m_pace != arg) { + m_pace = arg; + emit paceChanged(arg); + } +} + + +void setAffectedParameter(AffectableParameters arg) +{ + if (m_affectedParameter != arg) { + m_affectedParameter = arg; + emit affectedParameterChanged(arg); + } +} + +private: + WanderData* getData(int idx); + QHash<int, WanderData*> m_wanderData; + qreal m_xVariance; + qreal m_yVariance; + qreal m_pace; + AffectableParameters m_affectedParameter; +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // WANDERAFFECTOR_H diff --git a/src/particles/qtquickparticlesglobal_p.h b/src/particles/qtquickparticlesglobal_p.h new file mode 100644 index 0000000000..d7814f759d --- /dev/null +++ b/src/particles/qtquickparticlesglobal_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQuickParticles 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 QTQUICKPARTICLESGLOBAL_P_H +#define QTQUICKPARTICLESGLOBAL_P_H + +#include <QtCore/qglobal.h> + +// We only have private exports from this library + +#if defined(Q_OS_WIN) +# if defined(QT_MAKEDLL) /* create a Qt DLL library */ +# if defined(QT_BUILD_QUICKPARTICLES_LIB) +# define Q_QUICKPARTICLES_PRIVATE_EXPORT Q_DECL_EXPORT +# else +# define Q_QUICKPARTICLES_PRIVATE_EXPORT Q_DECL_IMPORT +# endif +# elif defined(QT_DLL) /* use a Qt DLL library */ +# define Q_QUICKPARTICLES_PRIVATE_EXPORT Q_DECL_IMPORT +# endif +#endif + +#if !defined(Q_QUICKPARTICLES_PRIVATE_EXPORT) +# if defined(QT_SHARED) +# define Q_QUICKPARTICLES_PRIVATE_EXPORT Q_DECL_EXPORT +# else +# define Q_QUICKPARTICLES_PRIVATE_EXPORT +# endif +#endif + +#endif // QTQUICKPARTICLESGLOBAL_P_H |