aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Alpert <alan.alpert@nokia.com>2011-09-09 17:21:14 +1000
committerQt by Nokia <qt-info@nokia.com>2011-09-13 03:18:45 +0200
commit4dba5720e03542e0989adad2461358074c7d0dee (patch)
tree17cec83c78b3b50ebd68f60d07bbb66509540f90
parenta9f0f3562171508c107c321a91520719e4d7c43a (diff)
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 <alan.alpert@nokia.com>
-rw-r--r--examples/declarative/flickr/content/ImageDetails.qml3
-rw-r--r--examples/declarative/particles/trails/turbulence.qml22
-rw-r--r--src/declarative/particles/defaultshaders/noise.pngbin0 -> 19477 bytes
-rw-r--r--src/declarative/particles/particles.qrc1
-rw-r--r--src/declarative/particles/qsgturbulence.cpp149
-rw-r--r--src/declarative/particles/qsgturbulence_p.h65
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
new file mode 100644
index 0000000000..3c723e1a5a
--- /dev/null
+++ b/src/declarative/particles/defaultshaders/noise.png
Binary files differ
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