diff options
-rw-r--r-- | examples/declarative/flickr/content/ImageDetails.qml | 3 | ||||
-rw-r--r-- | examples/declarative/particles/trails/turbulence.qml | 22 | ||||
-rw-r--r-- | src/declarative/particles/defaultshaders/noise.png | bin | 0 -> 19477 bytes | |||
-rw-r--r-- | src/declarative/particles/particles.qrc | 1 | ||||
-rw-r--r-- | src/declarative/particles/qsgturbulence.cpp | 149 | ||||
-rw-r--r-- | src/declarative/particles/qsgturbulence_p.h | 65 |
6 files changed, 129 insertions, 111 deletions
diff --git a/examples/declarative/flickr/content/ImageDetails.qml b/examples/declarative/flickr/content/ImageDetails.qml index 8d3cdfb494..3d64a33f87 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 62216c3460..104bb10e80 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 Binary files differnew file mode 100644 index 0000000000..3c723e1a5a --- /dev/null +++ b/src/declarative/particles/defaultshaders/noise.png diff --git a/src/declarative/particles/particles.qrc b/src/declarative/particles/particles.qrc index 5403f55266..db00a57453 100644 --- a/src/declarative/particles/particles.qrc +++ b/src/declarative/particles/particles.qrc @@ -6,5 +6,6 @@ <file>defaultshaders/spriteimagevertex.shader</file> <file>defaultshaders/identitytable.png</file> <file>defaultshaders/defaultFadeInOut.png</file> + <file>defaultshaders/noise.png</file> </qresource> </RCC> diff --git a/src/declarative/particles/qsgturbulence.cpp b/src/declarative/particles/qsgturbulence.cpp index 2caebc7c1f..c24af1ba67 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_gridSize; i++) + free(m_vectorField[i]); + free(m_vectorField); + } } static qreal magnitude(qreal x, qreal y) @@ -87,8 +108,12 @@ static qreal magnitude(qreal x, qreal y) return sqrt(x*x + y*y); } -void QSGTurbulenceAffector::setSize(int arg) +void QSGTurbulenceAffector::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++) @@ -96,81 +121,77 @@ void QSGTurbulenceAffector::setSize(int arg) 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; - emit sizeChanged(arg); } -} -void QSGTurbulenceAffector::ensureInit() -{ - if (m_inited) - return; - m_inited = true; - m_field = (QPointF**)malloc(m_gridSize * sizeof(QPointF*)); + m_field = (qreal**)malloc(m_gridSize * sizeof(qreal*)); for (int i=0; i<m_gridSize; i++) - m_field[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF)); + 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 = QImage(m_noiseSource.toLocalFile()).scaled(QSize(m_gridSize, m_gridSize)); + if (image.isNull()) + image = QImage(":defaultshaders/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] = QPointF(); - m_spacing = QPointF(width()/m_gridSize, height()/m_gridSize); - m_magSum = magnitude(m_spacing.x(), m_spacing.y())*2; + 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)); + } + } } -void QSGTurbulenceAffector::mapUpdate() +qreal QSGTurbulenceAffector::boundsRespectingField(int x, int y) { - QPoint pos(rand() % m_gridSize, rand() % m_gridSize); - QPointF vector(m_strength - (((qreal)rand() / RAND_MAX) * m_strength*2), - m_strength - (((qreal)rand() / RAND_MAX) * m_strength*2)); - for (int i = 0; i < m_gridSize; i++){ - for (int j = 0; j < m_gridSize; j++){ - qreal dist = magnitude(i-pos.x(), j-pos.y()); - m_field[i][j] += vector/(dist + 1); - if (magnitude(m_field[i][j].x(), m_field[i][j].y()) > 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<QPair<int, int> > 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<int, int> 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 dd938f1bca..110ecf4e4b 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 |