Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickturbulence.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtCore/qtconfigmacros.h>
5#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
6
8#include "qquickparticlepainter_p.h" //TODO: Why was this needed again?
9#include <cmath>
10#include <cstdlib>
11#include <QDebug>
12#include <QQmlFile>
13QT_BEGIN_NAMESPACE
14
15/*!
16 \qmltype Turbulence
17 \nativetype QQuickTurbulenceAffector
18 \inqmlmodule QtQuick.Particles
19 \ingroup qtquick-particles
20 \inherits ParticleAffector
21 \brief Provides fluid-like forces from a noise image.
22
23 The Turbulence Element scales the noise source over the area it affects,
24 and uses the curl of that source to generate force vectors.
25
26 Turbulence requires a fixed size. Unlike other affectors, a 0x0 Turbulence element
27 will affect no particles.
28
29 The source should be relatively smooth black and white noise, such as perlin noise.
30*/
31/*!
32 \qmlproperty real QtQuick.Particles::Turbulence::strength
33
34 The magnitude of the velocity vector at any point varies between zero and
35 the square root of two. It will then be multiplied by strength to get the
36 velocity per second for the particles affected by the turbulence.
37*/
38/*!
39 \qmlproperty url QtQuick.Particles::Turbulence::noiseSource
40
41 The source image to generate the turbulence from. It will be scaled to the size of the element,
42 so equal or larger sizes will give better results. Tweaking this image is the only way to tweak
43 behavior such as where vortices are or how many exist.
44
45 The source should be a relatively smooth black and white noise image, such as perlin noise.
46 A default image will be used if none is provided.
47*/
48
49QQuickTurbulenceAffector::QQuickTurbulenceAffector(QQuickItem *parent) :
50 QQuickParticleAffector(parent),
51 m_strength(10), m_gridSize(0), m_field(nullptr), m_vectorField(nullptr), m_inited(false)
52{
53}
54
55void QQuickTurbulenceAffector::geometryChange(const QRectF &, const QRectF &)
56{
57 initializeGrid();
58}
59
60QQuickTurbulenceAffector::~QQuickTurbulenceAffector()
61{
62 if (m_field) {
63 for (int i=0; i<m_gridSize; i++)
64 free(m_field[i]);
65 free(m_field);
66 }
67 if (m_vectorField) {
68 for (int i=0; i<m_gridSize; i++)
69 free(m_vectorField[i]);
70 free(m_vectorField);
71 }
72}
73
74void QQuickTurbulenceAffector::initializeGrid()
75{
76 if (!m_inited)
77 return;
78
79 int arg = qMax(width(), height());
80 if (m_gridSize != arg) {
81 if (m_field){ //deallocate and then reallocate grid
82 for (int i=0; i<m_gridSize; i++)
83 free(m_field[i]);
84 free(m_field);
85 }
86 if (m_vectorField) {
87 for (int i=0; i<m_gridSize; i++)
88 free(m_vectorField[i]);
89 free(m_vectorField);
90 }
91 m_gridSize = arg;
92 }
93
94 m_field = (qreal**)malloc(m_gridSize * sizeof(qreal*));
95 for (int i=0; i<m_gridSize; i++)
96 m_field[i] = (qreal*)malloc(m_gridSize * sizeof(qreal));
97 m_vectorField = (QPointF**)malloc(m_gridSize * sizeof(QPointF*));
98 for (int i=0; i<m_gridSize; i++)
99 m_vectorField[i] = (QPointF*)malloc(m_gridSize * sizeof(QPointF));
100
101 QImage image;
102 if (!m_noiseSource.isEmpty())
103 image = QImage(QQmlFile::urlToLocalFileOrQrc(m_noiseSource)).scaled(QSize(m_gridSize, m_gridSize));
104 if (image.isNull())
105 image = QImage(QStringLiteral(":particleresources/noise.png")).scaled(QSize(m_gridSize, m_gridSize));
106
107 for (int i=0; i<m_gridSize; i++)
108 for (int j=0; j<m_gridSize; j++)
109 m_field[i][j] = qGray(image.pixel(QPoint(i,j)));
110 for (int i=0; i<m_gridSize; i++){
111 for (int j=0; j<m_gridSize; j++){
112 m_vectorField[i][j].setX(boundsRespectingField(i-1,j) - boundsRespectingField(i,j));
113 m_vectorField[i][j].setY(boundsRespectingField(i,j) - boundsRespectingField(i,j-1));
114 }
115 }
116}
117
118qreal QQuickTurbulenceAffector::boundsRespectingField(int x, int y)
119{
120 if (x < 0)
121 x = 0;
122 if (x >= m_gridSize)
123 x = m_gridSize - 1;
124 if (y < 0)
125 y = 0;
126 if (y >= m_gridSize)
127 y = m_gridSize - 1;
128 return m_field[x][y];
129}
130
131void QQuickTurbulenceAffector::ensureInit()
132{
133 if (m_inited)
134 return;
135 m_inited = true;
136 initializeGrid();
137}
138
139void QQuickTurbulenceAffector::affectSystem(qreal dt)
140{
141 if (!m_system || !m_enabled)
142 return;
143 ensureInit();
144 if (!m_gridSize)
145 return;
146
147 updateOffsets();//### Needed if an ancestor is transformed.
148
149 QRect boundsRect(0,0,m_gridSize,m_gridSize);
150 for (QQuickParticleGroupData *gd : m_system->groupData) {
151 if (!activeGroup(gd->index))
152 continue;
153 foreach (QQuickParticleData *d, gd->data){
154 if (!shouldAffect(d))
155 continue;
156 QPoint pos = (QPointF(d->curX(m_system), d->curY(m_system)) - m_offset).toPoint();
157 if (!boundsRect.contains(pos,true))//Need to redo bounds checking due to quantization.
158 continue;
159 qreal fx = 0.0;
160 qreal fy = 0.0;
161 fx += m_vectorField[pos.x()][pos.y()].x() * m_strength;
162 fy += m_vectorField[pos.x()][pos.y()].y() * m_strength;
163 if (fx || fy){
164 d->setInstantaneousVX(d->curVX(m_system)+ fx * dt, m_system);
165 d->setInstantaneousVY(d->curVY(m_system)+ fy * dt, m_system);
166 postAffect(d);
167 }
168 }
169 }
170}
171
172QT_END_NAMESPACE
173
174#include "moc_qquickturbulence_p.cpp"