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