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