From: Alan Alpert Date: Fri, 9 Sep 2011 07:21:14 +0000 (+1000) Subject: Implement Turbulence properly X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=4dba5720e03542e0989adad2461358074c7d0dee;p=konrad%2Fqtdeclarative.git Implement Turbulence properly Or at least closer to. Now uses curl noise off of a source noise image, documented as usable, and not an immense performance drain (just a normal one). Change-Id: Iac11c98cd9589cbe6a41b2b30893ab40d541d18f Reviewed-on: http://codereview.qt-project.org/4510 Reviewed-by: Alan Alpert --- diff --git a/examples/declarative/flickr/content/ImageDetails.qml b/examples/declarative/flickr/content/ImageDetails.qml index 8d3cdfb..3d64a33 100644 --- a/examples/declarative/flickr/content/ImageDetails.qml +++ b/examples/declarative/flickr/content/ImageDetails.qml @@ -162,8 +162,7 @@ Flipable { id: turbulence system: imageSystem anchors.fill: parent - frequency: 100 - strength: 250 + strength: 240 enabled: false } diff --git a/examples/declarative/particles/trails/turbulence.qml b/examples/declarative/particles/trails/turbulence.qml index 62216c3..104bb10 100644 --- a/examples/declarative/particles/trails/turbulence.qml +++ b/examples/declarative/particles/trails/turbulence.qml @@ -56,15 +56,19 @@ Rectangle{ ParticleSystem{ id: ps } + MouseArea{ + anchors.fill: parent + onClicked: turb.enabled = !turb.enabled + } Turbulence{ + id: turb system: ps + enabled: true height: (parent.height / 2) width: parent.width / 2 x: parent. width / 4 anchors.fill: parent - strength: 16 - frequency: 64 - gridSize: 16 + strength: 32 } ImageParticle{ particles: ["smoke"] @@ -79,7 +83,7 @@ Rectangle{ source: "content/particle.png" color: "#11ff400f" colorVariation: 0.1 - } + } Emitter{ anchors.centerIn: parent system: ps @@ -96,12 +100,12 @@ Rectangle{ TrailEmitter{ id: smoke1 width: root.width - height: 258 + height: root.height/2 - 20 system: ps particle: "smoke" follow: "flame" - emitRatePerParticle: 4 + emitRatePerParticle: 1 lifeSpan: 2400 lifeSpanVariation: 400 size: 16 @@ -113,16 +117,16 @@ Rectangle{ TrailEmitter{ id: smoke2 width: root.width - height: 232 + height: root.height/2 - 40 system: ps particle: "smoke" follow: "flame" - emitRatePerParticle: 1 + emitRatePerParticle: 4 lifeSpan: 2400 size: 36 endSize: 24 - sizeVariation: 8 + sizeVariation: 12 acceleration: PointDirection{ y: -40 } speed: AngleDirection{ angle: 270; magnitude: 40; angleVariation: 22; magnitudeVariation: 5 } } diff --git a/src/declarative/particles/defaultshaders/noise.png b/src/declarative/particles/defaultshaders/noise.png new file mode 100644 index 0000000..3c723e1 Binary files /dev/null and b/src/declarative/particles/defaultshaders/noise.png differ diff --git a/src/declarative/particles/particles.qrc b/src/declarative/particles/particles.qrc index 5403f55..db00a57 100644 --- a/src/declarative/particles/particles.qrc +++ b/src/declarative/particles/particles.qrc @@ -6,5 +6,6 @@ defaultshaders/spriteimagevertex.shader defaultshaders/identitytable.png defaultshaders/defaultFadeInOut.png + defaultshaders/noise.png diff --git a/src/declarative/particles/qsgturbulence.cpp b/src/declarative/particles/qsgturbulence.cpp index 2caebc7..c24af1b 100644 --- a/src/declarative/particles/qsgturbulence.cpp +++ b/src/declarative/particles/qsgturbulence.cpp @@ -50,27 +50,43 @@ QT_BEGIN_NAMESPACE \qmlclass Turbulence QSGTurbulenceAffector \inqmlmodule QtQuick.Particles 2 \inherits Affector - \brief The TurbulenceAffector is a bit of a hack and probably shouldn't be used yet. + \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 int QtQuick.Particles2::Turbulence::strength - Maximum magnitude of a point in the vector field. -*/ -/*! - \qmlproperty int QtQuick.Particles2::Turbulence::frequency - Times per second vector field is perturbed. + \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 int QtQuick.Particles2::Turbulence::gridSize - Square root of the number of points in the vector field simulcrum. + \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. */ QSGTurbulenceAffector::QSGTurbulenceAffector(QSGItem *parent) : QSGParticleAffector(parent), - m_strength(10), m_lastT(0), m_frequency(64), m_gridSize(10), m_field(0), m_inited(false) + m_strength(10), m_lastT(0), m_gridSize(0), m_field(0), m_vectorField(0), m_inited(false) +{ +} + +void QSGTurbulenceAffector::geometryChanged(const QRectF &, const QRectF &) { - //TODO: Update grid on size change + initializeGrid(); } QSGTurbulenceAffector::~QSGTurbulenceAffector() @@ -80,6 +96,11 @@ QSGTurbulenceAffector::~QSGTurbulenceAffector() free(m_field[i]); free(m_field); } + if (m_vectorField) { + for (int i=0; i m_strength){ - //Speed limit - qreal theta = atan2(m_field[i][j].y(), m_field[i][j].x()); - m_field[i][j].setX(m_strength * cos(theta)); - m_field[i][j].setY(m_strength * sin(theta)); - } - } - } + 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 QSGTurbulenceAffector::ensureInit() +{ + if (m_inited) + return; + m_inited = true; + initializeGrid(); +} void QSGTurbulenceAffector::affectSystem(qreal dt) { if (!m_system || !m_enabled) return; ensureInit(); - qreal period = 1.0/m_frequency; - qreal time = m_system->m_timeInt / 1000.0; - while ( m_lastT < time ){ - mapUpdate(); - m_lastT += period; - } + QRectF boundsRect(0, 0, width()-1, height()-1); foreach (QSGParticleGroupData *gd, m_system->m_groupData){ if (!activeGroup(m_system->m_groupData.key(gd)))//TODO: Surely this can be done better - return; + continue; foreach (QSGParticleData *d, gd->data){ - if (!d || !activeGroup(d->group)) - return; + if (!d || !activeGroup(d->group) || !d->stillAlive()) + continue; + QPoint pos = (QPointF(d->curX(), d->curY()) - m_offset).toPoint(); + if (!boundsRect.contains(pos)) + continue; qreal fx = 0.0; qreal fy = 0.0; - QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset - QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y()); - QSet > nodes; - nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y())); - nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y())); - nodes << qMakePair((int)floor(nodePos.x()), (int)ceil(nodePos.y())); - nodes << qMakePair((int)floor(nodePos.x()), (int)floor(nodePos.y())); - typedef QPair intPair; - foreach (const intPair &p, nodes){ - if (!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second))) - continue; - qreal dist = magnitude(pos.x() - p.first*m_spacing.x(), pos.y() - p.second*m_spacing.y());//TODO: Mathematically valid - fx += m_field[p.first][p.second].x() * ((m_magSum - dist)/m_magSum);//Proportionally weight nodes - fy += m_field[p.first][p.second].y() * ((m_magSum - dist)/m_magSum); - } + 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); diff --git a/src/declarative/particles/qsgturbulence_p.h b/src/declarative/particles/qsgturbulence_p.h index dd938f1..110ecf4 100644 --- a/src/declarative/particles/qsgturbulence_p.h +++ b/src/declarative/particles/qsgturbulence_p.h @@ -56,68 +56,61 @@ class QSGParticlePainter; class QSGTurbulenceAffector : public QSGParticleAffector { Q_OBJECT - Q_PROPERTY(int strength READ strength WRITE setStrength NOTIFY strengthChanged) - Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged) - Q_PROPERTY(int gridSize READ size WRITE setSize NOTIFY sizeChanged) -public: + Q_PROPERTY(qreal strength READ strength WRITE setStrength NOTIFY strengthChanged) + Q_PROPERTY(QUrl noiseSource READ noiseSource WRITE setNoiseSource NOTIFY noiseSourceChanged) + public: explicit QSGTurbulenceAffector(QSGItem *parent = 0); ~QSGTurbulenceAffector(); virtual void affectSystem(qreal dt); - int strength() const + qreal strength() const { return m_strength; } - int frequency() const + QUrl noiseSource() const { - return m_frequency; + return m_noiseSource; } - - int size() const - { - return m_gridSize; - } - signals: - void strengthChanged(int arg); - - void frequencyChanged(int arg); + void strengthChanged(qreal arg); - void sizeChanged(int arg); + void noiseSourceChanged(QUrl arg); public slots: + void initializeGrid(); -void setStrength(int arg) -{ - if (m_strength != arg) { - m_strength = arg; - emit strengthChanged(arg); + void setStrength(qreal arg) + { + if (m_strength != arg) { + m_strength = arg; + emit strengthChanged(arg); + } } -} -void setFrequency(int arg) -{ - if (m_frequency != arg) { - m_frequency = arg; - emit frequencyChanged(arg); + void setNoiseSource(QUrl arg) + { + if (m_noiseSource != arg) { + m_noiseSource = arg; + emit noiseSourceChanged(arg); + } } -} - -void setSize(int arg); +protected: + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); private: void ensureInit(); void mapUpdate(); - int m_strength; + qreal boundsRespectingField(int x, int y); + qreal m_strength; qreal m_lastT; - int m_frequency; int m_gridSize; - QPointF** m_field; - QPointF m_spacing; - qreal m_magSum; + qreal** m_field; + QPointF** m_vectorField; bool m_inited; + QUrl m_noiseSource; }; QT_END_NAMESPACE