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 // without threads, QtConcurrent::run() would never run the task
175 if (m_asynchronous && QT_CONFIG(thread)) {
176 m_geometryDataFuture = QtConcurrent::run(generateTorusGeometryAsync,
177 m_rings,
178 m_segments,
179 m_radius,
180 m_tubeRadius);
181 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
182 m_status = Status::Loading;
183 Q_EMIT statusChanged();
184 } else {
185#else
186 {
187
188#endif // QT_CONFIG(concurrent)
189 updateGeometry(generateTorusGeometry(m_rings, m_segments, m_radius, m_tubeRadius));
190 }
191}
192
193void TorusGeometry::requestFinished()
194{
195#if QT_CONFIG(concurrent)
196 const auto output = m_geometryDataFuture.takeResult();
197 updateGeometry(output);
198#endif
199}
200
201void TorusGeometry::scheduleGeometryUpdate()
202{
203 if (!m_geometryUpdateRequested) {
204 QMetaObject::invokeMethod(this, "doUpdateGeometry", Qt::QueuedConnection);
205 m_geometryUpdateRequested = true;
206 }
207}
208
209void TorusGeometry::updateGeometry(const GeometryData &geometryData)
210{
211 clear();
212 setStride(sizeof(float) * 8); // 3 for position, 3 for normal, 2 for uv0
213 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
214 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
215 0,
216 QQuick3DGeometry::Attribute::F32Type);
217 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
218 3 * sizeof(float),
219 QQuick3DGeometry::Attribute::F32Type);
220 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
221 6 * sizeof(float),
222 QQuick3DGeometry::Attribute::F32Type);
223 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
224 0,
225 QQuick3DGeometry::Attribute::U32Type);
226
227 setBounds(geometryData.boundsMin, geometryData.boundsMax);
228 setVertexData(geometryData.vertexData);
229 setIndexData(geometryData.indexData);
230
231 // If the geometry update was requested while the geometry was being generated asynchronously,
232 // we need to schedule another geometry update now that the geometry is ready.
233 if (m_pendingAsyncUpdate) {
234 m_pendingAsyncUpdate = false;
235 scheduleGeometryUpdate();
236 } else {
237 m_status = Status::Ready;
238 Q_EMIT statusChanged();
239 }
240 update();
241 emit geometryChanged();
242}
243
244const size_t FLOAT_SIZE = sizeof(float);
245const size_t VEC3_SIZE = sizeof(QVector3D);
246const size_t VEC2_SIZE = sizeof(QVector2D);
247const size_t UINT_SIZE = sizeof(quint32);
248
249TorusGeometry::GeometryData TorusGeometry::generateTorusGeometry(int rings, int segments, float radius, float tubeRadius)
250{
251 GeometryData geomData;
252
253 // Bounds initialization
254 QVector3D boundsMin(std::numeric_limits<float>::max(),
255 std::numeric_limits<float>::max(),
256 std::numeric_limits<float>::max());
257
258 QVector3D boundsMax(std::numeric_limits<float>::lowest(),
259 std::numeric_limits<float>::lowest(),
260 std::numeric_limits<float>::lowest());
261
262 // Pre-calculate the number of vertices and indices
263 int numVerts = (rings + 1) * (segments + 1);
264 int numIndices = rings * segments * 6; // Two triangles per quad
265
266 // Reserve space in the QByteArrays (vertex data: position (3 floats), normal (3 floats), UV (2 floats))
267 int vertexDataSize = numVerts * (VEC3_SIZE + VEC3_SIZE + VEC2_SIZE); // Position, Normal, UV
268 geomData.vertexData.resize(vertexDataSize);
269
270 // Indices are stored as unsigned 32-bit integers
271 int indexDataSize = numIndices * UINT_SIZE;
272 geomData.indexData.resize(indexDataSize);
273
274 // Get raw pointers to the QByteArray data
275 char* vertexPtr = geomData.vertexData.data();
276 char* indexPtr = geomData.indexData.data();
277
278 int vertexOffset = 0;
279 int indexOffset = 0;
280
281 // Iterate over the rings and segments to compute vertices, normals, and UVs
282 for (int i = 0; i <= rings; ++i) {
283 for (int j = 0; j <= segments; ++j) {
284 float u = (i / (float)rings) * M_PI * 2;
285 float v = (j / (float)segments) * M_PI * 2;
286
287 float centerX = radius * qCos(u);
288 float centerZ = radius * qSin(u);
289
290 float posX = centerX + tubeRadius * qCos(v) * qCos(u);
291 float posY = tubeRadius * qSin(v);
292 float posZ = centerZ + tubeRadius * qCos(v) * qSin(u);
293
294 // Update bounds
295 boundsMin.setX(qMin(boundsMin.x(), posX));
296 boundsMin.setY(qMin(boundsMin.y(), posY));
297 boundsMin.setZ(qMin(boundsMin.z(), posZ));
298
299 boundsMax.setX(qMax(boundsMax.x(), posX));
300 boundsMax.setY(qMax(boundsMax.y(), posY));
301 boundsMax.setZ(qMax(boundsMax.z(), posZ));
302
303 // Position data (3 floats)
304 memcpy(vertexPtr + vertexOffset, &posX, FLOAT_SIZE);
305 memcpy(vertexPtr + vertexOffset + FLOAT_SIZE, &posY, FLOAT_SIZE);
306 memcpy(vertexPtr + vertexOffset + 2 * FLOAT_SIZE, &posZ, FLOAT_SIZE);
307 vertexOffset += 3 * FLOAT_SIZE;
308
309 // Calculate normal
310 QVector3D normal = QVector3D(posX - centerX, posY, posZ - centerZ).normalized();
311
312 // Normal data (3 floats)
313 memcpy(vertexPtr + vertexOffset, &normal, VEC3_SIZE);
314 vertexOffset += VEC3_SIZE;
315
316 // UV data (2 floats)
317 float uvX = 1.0f - (i / (float)rings);
318 float uvY = j / (float)segments;
319 memcpy(vertexPtr + vertexOffset, &uvX, FLOAT_SIZE);
320 memcpy(vertexPtr + vertexOffset + FLOAT_SIZE, &uvY, FLOAT_SIZE);
321 vertexOffset += 2 * FLOAT_SIZE;
322 }
323 }
324
325 // Generate indices
326 for (int i = 0; i < rings; ++i) {
327 for (int j = 0; j < segments; ++j) {
328 int a = (segments + 1) * i + j;
329 int b = (segments + 1) * (i + 1) + j;
330 int c = (segments + 1) * (i + 1) + j + 1;
331 int d = (segments + 1) * i + j + 1;
332
333 // First triangle (a, d, b)
334 memcpy(indexPtr + indexOffset, &a, UINT_SIZE);
335 memcpy(indexPtr + indexOffset + UINT_SIZE, &d, UINT_SIZE);
336 memcpy(indexPtr + indexOffset + 2 * UINT_SIZE, &b, UINT_SIZE);
337 indexOffset += 3 * UINT_SIZE;
338
339 // Second triangle (b, d, c)
340 memcpy(indexPtr + indexOffset, &b, UINT_SIZE);
341 memcpy(indexPtr + indexOffset + UINT_SIZE, &d, UINT_SIZE);
342 memcpy(indexPtr + indexOffset + 2 * UINT_SIZE, &c, UINT_SIZE);
343 indexOffset += 3 * UINT_SIZE;
344 }
345 }
346
347 // Set the computed bounds
348 geomData.boundsMin = boundsMin;
349 geomData.boundsMax = boundsMax;
350
351 return geomData;
352}
353
354#if QT_CONFIG(concurrent)
355void TorusGeometry::generateTorusGeometryAsync(QPromise<TorusGeometry::GeometryData> &promise, int rings, int segments, float radius, float tubeRadius)
356{
357 auto output = generateTorusGeometry(rings, segments, radius, tubeRadius);
358 promise.addResult(output);
359}
360#endif
361
362QT_END_NAMESPACE
const size_t UINT_SIZE
const size_t VEC3_SIZE
const size_t VEC2_SIZE
const size_t FLOAT_SIZE