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