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
torusgeometry.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
7#include <limits>
8
9#if QT_CONFIG(concurrent)
10#include <QtConcurrentRun>
11#endif
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 \qmltype TorusGeometry
17 \inqmlmodule QtQuick3D.Helpers
18 \inherits Geometry
19 \since 6.9
20 \brief Provides geometry for a torus.
21
22 TorusGeometry is a geometry type that represents a torus. The torus is defined by the number of
23 rings and segments, and the radius of the torus.
24
25*/
26
27/*!
28 \qmlproperty int TorusGeometry::rings
29 Specifies the number of rings in the torus. The default value is 50.
30*/
31
32/*!
33 \qmlproperty int TorusGeometry::segments
34 Specifies the number of segments in the torus. The default value is 50.
35*/
36
37/*!
38 \qmlproperty real TorusGeometry::radius
39 Specifies the radius of the torus. The default value is 100.
40*/
41
42/*!
43 \qmlproperty real TorusGeometry::tubeRadius
44 Specifies the radius of the tube of the torus. The default value is 10.
45*/
46
47/*!
48 \qmlproperty bool TorusGeometry::asynchronous
49
50 This property holds whether the geometry generation should be asynchronous.
51*/
52
53/*!
54 \qmlproperty bool TorusGeometry::status
55 \readonly
56
57 This property holds the status of the geometry generation when asynchronous is true.
58
59 \value TorusGeometry.Null The geometry generation has not started
60 \value TorusGeometry.Ready The geometry generation is complete.
61 \value TorusGeometry.Loading The geometry generation is in progress.
62 \value TorusGeometry.Error The geometry generation failed.
63*/
64
65TorusGeometry::TorusGeometry(QQuick3DObject *parent)
66 : QQuick3DGeometry(parent)
67{
68#if QT_CONFIG(concurrent)
69 connect(&m_geometryDataWatcher, &QFutureWatcher<GeometryData>::finished, this, &TorusGeometry::requestFinished);
70#endif
71 scheduleGeometryUpdate();
72}
73
74TorusGeometry::~TorusGeometry()
75{
76
77}
78
79int TorusGeometry::rings() const
80{
81 return m_rings;
82}
83
84void TorusGeometry::setRings(int newRings)
85{
86 if (m_rings == newRings)
87 return;
88 m_rings = newRings;
89 emit ringsChanged();
90 scheduleGeometryUpdate();
91}
92
93int TorusGeometry::segments() const
94{
95 return m_segments;
96}
97
98void TorusGeometry::setSegments(int newSegments)
99{
100 if (m_segments == newSegments)
101 return;
102 m_segments = newSegments;
103 emit segmentsChanged();
104 scheduleGeometryUpdate();
105}
106
107float TorusGeometry::radius() const
108{
109 return m_radius;
110}
111
112void TorusGeometry::setRadius(float newRadius)
113{
114 if (qFuzzyCompare(m_radius, newRadius))
115 return;
116 m_radius = newRadius;
117 emit radiusChanged();
118 scheduleGeometryUpdate();
119}
120
121float TorusGeometry::tubeRadius() const
122{
123 return m_tubeRadius;
124}
125
126void TorusGeometry::setTubeRadius(float newTubeRadius)
127{
128 if (qFuzzyCompare(m_tubeRadius, newTubeRadius))
129 return;
130 m_tubeRadius = newTubeRadius;
131 emit tubeRadiusChanged();
132 scheduleGeometryUpdate();
133}
134
135bool TorusGeometry::asynchronous() const
136{
137 return m_asynchronous;
138}
139
140void TorusGeometry::setAsynchronous(bool newAsynchronous)
141{
142 if (m_asynchronous == newAsynchronous)
143 return;
144 m_asynchronous = newAsynchronous;
145 emit asynchronousChanged();
146}
147
148TorusGeometry::Status TorusGeometry::status() const
149{
150 return m_status;
151}
152
153void TorusGeometry::doUpdateGeometry()
154{
155 // reset the flag since we are processing the update
156 m_geometryUpdateRequested = false;
157
158#if QT_CONFIG(concurrent)
159 if (m_geometryDataFuture.isRunning()) {
160 m_pendingAsyncUpdate = true;
161 return;
162 }
163#endif
164
165 // Check for validity of the parameters before generation
166 if (m_rings <= 0 || m_segments <= 0 || m_radius <= 0 || m_tubeRadius <= 0) {
167 clear();
168 update();
169 return;
170 }
171
172#if QT_CONFIG(concurrent)
173
174 if (m_asynchronous) {
175 m_geometryDataFuture = QtConcurrent::run(generateTorusGeometryAsync,
176 m_rings,
177 m_segments,
178 m_radius,
179 m_tubeRadius);
180 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
181 m_status = Status::Loading;
182 Q_EMIT statusChanged();
183 } else {
184#else
185 {
186
187#endif // QT_CONFIG(concurrent)
188 updateGeometry(generateTorusGeometry(m_rings, m_segments, m_radius, m_tubeRadius));
189 }
190}
191
192void TorusGeometry::requestFinished()
193{
194#if QT_CONFIG(concurrent)
195 const auto output = m_geometryDataFuture.takeResult();
196 updateGeometry(output);
197#endif
198}
199
200void TorusGeometry::scheduleGeometryUpdate()
201{
202 if (!m_geometryUpdateRequested) {
203 QMetaObject::invokeMethod(this, "doUpdateGeometry", Qt::QueuedConnection);
204 m_geometryUpdateRequested = true;
205 }
206}
207
208void TorusGeometry::updateGeometry(const GeometryData &geometryData)
209{
210 clear();
211 setStride(sizeof(float) * 8); // 3 for position, 3 for normal, 2 for uv0
212 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
213 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
214 0,
215 QQuick3DGeometry::Attribute::F32Type);
216 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
217 3 * sizeof(float),
218 QQuick3DGeometry::Attribute::F32Type);
219 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
220 6 * sizeof(float),
221 QQuick3DGeometry::Attribute::F32Type);
222 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
223 0,
224 QQuick3DGeometry::Attribute::U32Type);
225
226 setBounds(geometryData.boundsMin, geometryData.boundsMax);
227 setVertexData(geometryData.vertexData);
228 setIndexData(geometryData.indexData);
229
230 // If the geometry update was requested while the geometry was being generated asynchronously,
231 // we need to schedule another geometry update now that the geometry is ready.
232 if (m_pendingAsyncUpdate) {
233 m_pendingAsyncUpdate = false;
234 scheduleGeometryUpdate();
235 } else {
236 m_status = Status::Ready;
237 Q_EMIT statusChanged();
238 }
239 update();
240 emit geometryChanged();
241}
242
243const size_t FLOAT_SIZE = sizeof(float);
244const size_t VEC3_SIZE = sizeof(QVector3D);
245const size_t VEC2_SIZE = sizeof(QVector2D);
246const size_t UINT_SIZE = sizeof(quint32);
247
248TorusGeometry::GeometryData TorusGeometry::generateTorusGeometry(int rings, int segments, float radius, float tubeRadius)
249{
250 GeometryData geomData;
251
252 // Bounds initialization
253 QVector3D boundsMin(std::numeric_limits<float>::max(),
254 std::numeric_limits<float>::max(),
255 std::numeric_limits<float>::max());
256
257 QVector3D boundsMax(std::numeric_limits<float>::lowest(),
258 std::numeric_limits<float>::lowest(),
259 std::numeric_limits<float>::lowest());
260
261 // Pre-calculate the number of vertices and indices
262 int numVerts = (rings + 1) * (segments + 1);
263 int numIndices = rings * segments * 6; // Two triangles per quad
264
265 // Reserve space in the QByteArrays (vertex data: position (3 floats), normal (3 floats), UV (2 floats))
266 int vertexDataSize = numVerts * (VEC3_SIZE + VEC3_SIZE + VEC2_SIZE); // Position, Normal, UV
267 geomData.vertexData.resize(vertexDataSize);
268
269 // Indices are stored as unsigned 32-bit integers
270 int indexDataSize = numIndices * UINT_SIZE;
271 geomData.indexData.resize(indexDataSize);
272
273 // Get raw pointers to the QByteArray data
274 char* vertexPtr = geomData.vertexData.data();
275 char* indexPtr = geomData.indexData.data();
276
277 int vertexOffset = 0;
278 int indexOffset = 0;
279
280 // Iterate over the rings and segments to compute vertices, normals, and UVs
281 for (int i = 0; i <= rings; ++i) {
282 for (int j = 0; j <= segments; ++j) {
283 float u = (i / (float)rings) * M_PI * 2;
284 float v = (j / (float)segments) * M_PI * 2;
285
286 float centerX = radius * qCos(u);
287 float centerZ = radius * qSin(u);
288
289 float posX = centerX + tubeRadius * qCos(v) * qCos(u);
290 float posY = tubeRadius * qSin(v);
291 float posZ = centerZ + tubeRadius * qCos(v) * qSin(u);
292
293 // Update bounds
294 boundsMin.setX(qMin(boundsMin.x(), posX));
295 boundsMin.setY(qMin(boundsMin.y(), posY));
296 boundsMin.setZ(qMin(boundsMin.z(), posZ));
297
298 boundsMax.setX(qMax(boundsMax.x(), posX));
299 boundsMax.setY(qMax(boundsMax.y(), posY));
300 boundsMax.setZ(qMax(boundsMax.z(), posZ));
301
302 // Position data (3 floats)
303 memcpy(vertexPtr + vertexOffset, &posX, FLOAT_SIZE);
304 memcpy(vertexPtr + vertexOffset + FLOAT_SIZE, &posY, FLOAT_SIZE);
305 memcpy(vertexPtr + vertexOffset + 2 * FLOAT_SIZE, &posZ, FLOAT_SIZE);
306 vertexOffset += 3 * FLOAT_SIZE;
307
308 // Calculate normal
309 QVector3D normal = QVector3D(posX - centerX, posY, posZ - centerZ).normalized();
310
311 // Normal data (3 floats)
312 memcpy(vertexPtr + vertexOffset, &normal, VEC3_SIZE);
313 vertexOffset += VEC3_SIZE;
314
315 // UV data (2 floats)
316 float uvX = 1.0f - (i / (float)rings);
317 float uvY = j / (float)segments;
318 memcpy(vertexPtr + vertexOffset, &uvX, FLOAT_SIZE);
319 memcpy(vertexPtr + vertexOffset + FLOAT_SIZE, &uvY, FLOAT_SIZE);
320 vertexOffset += 2 * FLOAT_SIZE;
321 }
322 }
323
324 // Generate indices
325 for (int i = 0; i < rings; ++i) {
326 for (int j = 0; j < segments; ++j) {
327 int a = (segments + 1) * i + j;
328 int b = (segments + 1) * (i + 1) + j;
329 int c = (segments + 1) * (i + 1) + j + 1;
330 int d = (segments + 1) * i + j + 1;
331
332 // First triangle (a, d, b)
333 memcpy(indexPtr + indexOffset, &a, UINT_SIZE);
334 memcpy(indexPtr + indexOffset + UINT_SIZE, &d, UINT_SIZE);
335 memcpy(indexPtr + indexOffset + 2 * UINT_SIZE, &b, UINT_SIZE);
336 indexOffset += 3 * UINT_SIZE;
337
338 // Second triangle (b, d, c)
339 memcpy(indexPtr + indexOffset, &b, UINT_SIZE);
340 memcpy(indexPtr + indexOffset + UINT_SIZE, &d, UINT_SIZE);
341 memcpy(indexPtr + indexOffset + 2 * UINT_SIZE, &c, UINT_SIZE);
342 indexOffset += 3 * UINT_SIZE;
343 }
344 }
345
346 // Set the computed bounds
347 geomData.boundsMin = boundsMin;
348 geomData.boundsMax = boundsMax;
349
350 return geomData;
351}
352
353#if QT_CONFIG(concurrent)
354void TorusGeometry::generateTorusGeometryAsync(QPromise<TorusGeometry::GeometryData> &promise, int rings, int segments, float radius, float tubeRadius)
355{
356 auto output = generateTorusGeometry(rings, segments, radius, tubeRadius);
357 promise.addResult(output);
358}
359#endif
360
361QT_END_NAMESPACE
const size_t UINT_SIZE
const size_t VEC3_SIZE
const size_t VEC2_SIZE
const size_t FLOAT_SIZE