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