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
cuboidgeometry.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
6#if QT_CONFIG(concurrent)
7#include <QtConcurrentRun>
8#endif
9
11
12/*!
13 \qmltype CuboidGeometry
14 \inqmlmodule QtQuick3D.Helpers
15 \inherits Geometry
16 \since 6.9
17 \brief Provides geometry for a cuboid.
18
19 CuboidGeometry is a geometry type that represents a cuboid. The cuboid's size is
20 defined by its xExtent, yExtent, and zExtent properties. The topology of the plane is defined by
21 the yzMeshResolution, xzMeshResolution, and xyMeshResolution properties.
22*/
23
24/*!
25 \qmlproperty float CuboidGeometry::xExtent
26 The x extent of the cuboid. The default value is 100.0.
27*/
28
29/*!
30 \qmlproperty float CuboidGeometry::yExtent
31 The y extent of the cuboid. The default value is 100.0.
32*/
33
34/*!
35 \qmlproperty float CuboidGeometry::zExtent
36 The z extent of the cuboid. The default value is 100.0.
37*/
38
39/*!
40 \qmlproperty size CuboidGeometry::yzMeshResolution
41 The number of segments in the y and z direction. The default value is 2x2.
42*/
43
44/*!
45 \qmlproperty size CuboidGeometry::xzMeshResolution
46 The number of segments in the x and z direction. The default value is 2x2.
47*/
48
49/*!
50 \qmlproperty size CuboidGeometry::xyMeshResolution
51 The number of segments in the x and y direction. The default value is 2x2.
52*/
53
54/*!
55 \qmlproperty bool CuboidGeometry::asynchronous
56
57 This property holds whether the geometry generation should be asynchronous.
58*/
59
60/*!
61 \qmlproperty bool CuboidGeometry::status
62 \readonly
63
64 This property holds the status of the geometry generation when asynchronous is true.
65
66 \value CuboidGeometry.Null The geometry generation has not started
67 \value CuboidGeometry.Ready The geometry generation is complete.
68 \value CuboidGeometry.Loading The geometry generation is in progress.
69 \value CuboidGeometry.Error The geometry generation failed.
70*/
71
72
73CuboidGeometry::CuboidGeometry(QQuick3DObject *parent)
75{
76#if QT_CONFIG(concurrent)
77 connect(&m_geometryDataWatcher, &QFutureWatcher<GeometryData>::finished, this, &CuboidGeometry::requestFinished);
78#endif
79 scheduleGeometryUpdate();
80}
81
82CuboidGeometry::~CuboidGeometry()
83{
84
85}
86
87float CuboidGeometry::xExtent() const
88{
89 return m_xExtent;
90}
91
92void CuboidGeometry::setXExtent(float newXExtent)
93{
94 if (qFuzzyCompare(m_xExtent, newXExtent))
95 return;
96 m_xExtent = newXExtent;
97 emit xExtentChanged();
98 scheduleGeometryUpdate();
99}
100
101float CuboidGeometry::yExtent() const
102{
103 return m_yExtent;
104}
105
106void CuboidGeometry::setYExtent(float newYExtent)
107{
108 if (qFuzzyCompare(m_yExtent, newYExtent))
109 return;
110 m_yExtent = newYExtent;
111 emit yExtentChanged();
112 scheduleGeometryUpdate();
113}
114
115float CuboidGeometry::zExtent() const
116{
117 return m_zExtent;
118}
119
120void CuboidGeometry::setZExtent(float newZExtent)
121{
122 if (qFuzzyCompare(m_zExtent, newZExtent))
123 return;
124 m_zExtent = newZExtent;
125 emit zExtentChanged();
126 scheduleGeometryUpdate();
127}
128
129QSize CuboidGeometry::yzMeshResolution() const
130{
131 return m_yzMeshResolution;
132}
133
134void CuboidGeometry::setYzMeshResolution(const QSize &newYzMeshResolution)
135{
136 if (m_yzMeshResolution == newYzMeshResolution)
137 return;
138 m_yzMeshResolution = newYzMeshResolution;
139 emit yzMeshResolutionChanged();
140 scheduleGeometryUpdate();
141}
142
143QSize CuboidGeometry::xzMeshResolution() const
144{
145 return m_xzMeshResolution;
146}
147
148void CuboidGeometry::setXzMeshResolution(const QSize &newXzMeshResolution)
149{
150 if (m_xzMeshResolution == newXzMeshResolution)
151 return;
152 m_xzMeshResolution = newXzMeshResolution;
153 emit xzMeshResolutionChanged();
154 scheduleGeometryUpdate();
155}
156
157QSize CuboidGeometry::xyMeshResolution() const
158{
159 return m_xyMeshResolution;
160}
161
162void CuboidGeometry::setXyMeshResolution(const QSize &newXyMeshResolution)
163{
164 if (m_xyMeshResolution == newXyMeshResolution)
165 return;
166 m_xyMeshResolution = newXyMeshResolution;
167 emit xyMeshResolutionChanged();
168 scheduleGeometryUpdate();
169}
170
171bool CuboidGeometry::asynchronous() const
172{
173 return m_asynchronous;
174}
175
176void CuboidGeometry::setAsynchronous(bool newAsynchronous)
177{
178 if (m_asynchronous == newAsynchronous)
179 return;
180 m_asynchronous = newAsynchronous;
181 emit asynchronousChanged();
182}
183
184CuboidGeometry::Status CuboidGeometry::status() const
185{
186 return m_status;
187}
188
189void CuboidGeometry::doUpdateGeometry()
190{
191 // reset the flag since we are processing the update
192 m_geometryUpdateRequested = false;
193
194#if QT_CONFIG(concurrent)
195 if (m_geometryDataFuture.isRunning()) {
196 m_pendingAsyncUpdate = true;
197 return;
198 }
199#endif
200
201
202
203 // Check validity of the geometry parameters
204 if (m_xExtent <= 0 || m_yExtent <= 0 || m_yExtent <= 0) {
205 clear();
206 update();
207 return;
208 }
209
210#if QT_CONFIG(concurrent)
211 if (m_asynchronous) {
212 m_geometryDataFuture = QtConcurrent::run(generateCuboidGeometryAsync,
213 m_xExtent,
214 m_yExtent,
215 m_zExtent,
216 m_yzMeshResolution,
217 m_xzMeshResolution,
218 m_xyMeshResolution);
219 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
220 m_status = Status::Loading;
221 Q_EMIT statusChanged();
222 } else {
223#else
224 {
225
226#endif // QT_CONFIG(concurrent)
227 updateGeometry(generateCuboidGeometry(m_xExtent,
228 m_yExtent,
229 m_zExtent,
230 m_yzMeshResolution,
231 m_xzMeshResolution,
232 m_xyMeshResolution));
233 }
234}
235
236void CuboidGeometry::requestFinished()
237{
238#if QT_CONFIG(concurrent)
239 const auto output = m_geometryDataFuture.takeResult();
240 updateGeometry(output);
241#endif
242}
243
244void CuboidGeometry::scheduleGeometryUpdate()
245{
246 if (!m_geometryUpdateRequested) {
247 QMetaObject::invokeMethod(this, "doUpdateGeometry", Qt::QueuedConnection);
248 m_geometryUpdateRequested = true;
249 }
250}
251
252void CuboidGeometry::updateGeometry(const GeometryData &geometryData)
253{
254 setStride(sizeof(float) * 8); // 3 for position, 2 for uv0, 3 for normal
255 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
256 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
257 0,
258 QQuick3DGeometry::Attribute::F32Type);
259 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
260 3 * sizeof(float),
261 QQuick3DGeometry::Attribute::F32Type);
262 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
263 5 * sizeof(float),
264 QQuick3DGeometry::Attribute::F32Type);
265 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
266 0,
267 QQuick3DGeometry::Attribute::U16Type);
268
269 setBounds(geometryData.boundsMin, geometryData.boundsMax);
270 setVertexData(geometryData.vertexData);
271 setIndexData(geometryData.indexData);
272
273 // If the geometry update was requested while the geometry was being generated asynchronously,
274 // we need to schedule another geometry update now that the geometry is ready.
275 if (m_pendingAsyncUpdate) {
276 m_pendingAsyncUpdate = false;
277 scheduleGeometryUpdate();
278 } else {
279 m_status = Status::Ready;
280 Q_EMIT statusChanged();
281 }
282 update();
283}
284
285CuboidGeometry::GeometryData CuboidGeometry::generateCuboidGeometry(float xExtent, float yExtent, float zExtent, QSize yzMeshResolution, QSize xzMeshResolution, QSize xyMeshResolution)
286{
287 GeometryData geometryData;
288
289 const float halfXExtent = xExtent / 2.0f;
290 const float halfYExtent = yExtent / 2.0f;
291 const float halfZExtent = zExtent / 2.0f;
292 geometryData.boundsMin = QVector3D(-halfXExtent, -halfYExtent, -halfZExtent);
293 geometryData.boundsMax = QVector3D(halfXExtent, halfYExtent, halfZExtent);
294
295 // Total number of vertices and indices required
296 int numVertices = (yzMeshResolution.width() + 1) * (yzMeshResolution.height() + 1) * 2 +
297 (xzMeshResolution.width() + 1) * (xzMeshResolution.height() + 1) * 2 +
298 (xyMeshResolution.width() + 1) * (xyMeshResolution.height() + 1) * 2;
299
300 int numIndices = yzMeshResolution.width() * yzMeshResolution.height() * 6 * 2 +
301 xzMeshResolution.width() * xzMeshResolution.height() * 6 * 2 +
302 xyMeshResolution.width() * xyMeshResolution.height() * 6 * 2;
303
304 const int vertexStride = sizeof(float) * (3 + 2 + 3); // vec3 (position), vec2 (uv), vec3 (normal)
305 const int indexStride = sizeof(uint16_t); // 16-bit index
306
307 // Allocate memory for vertex and index data
308 geometryData.vertexData.resize(numVertices * vertexStride);
309 geometryData.indexData.resize(numIndices * indexStride);
310
311 // Set up raw pointers for direct memory manipulation
312 float* vertexPtr = reinterpret_cast<float*>(geometryData.vertexData.data());
313 uint16_t* indexPtr = reinterpret_cast<uint16_t*>(geometryData.indexData.data());
314
315 // Inline lambda for plane generation
316 auto generatePlane = [](float* &vertexPtr, uint16_t* &indexPtr, uint16_t &indexOffset, float width, float height, const QVector3D& origin, const QVector3D& right, const QVector3D& up, const QVector3D& normal, QSize resolution, bool flipWinding = false) {
317 const int quadsX = resolution.width();
318 const int quadsY = resolution.height();
319 const float halfWidth = width / 2.0f;
320 const float halfHeight = height / 2.0f;
321 quint16 vertexCount = 0;
322
323 // Generate vertices
324 for (int y = 0; y <= quadsY; ++y) {
325 for (int x = 0; x <= quadsX; ++x) {
326 // Normalized UV coordinates
327 float u = static_cast<float>(x) / quadsX;
328 float v = static_cast<float>(y) / quadsY;
329
330 // Compute the position of the vertex
331 QVector3D position = origin + right * (u * width - halfWidth) + up * (v * height - halfHeight);
332
333 // Write position
334 *vertexPtr++ = position.x();
335 *vertexPtr++ = position.y();
336 *vertexPtr++ = position.z();
337
338 // Write UV coordinates
339 *vertexPtr++ = u;
340 *vertexPtr++ = v;
341
342 // Write normal
343 *vertexPtr++ = normal.x();
344 *vertexPtr++ = normal.y();
345 *vertexPtr++ = normal.z();
346
347 ++vertexCount;
348 }
349 }
350
351 // Generate indices
352 for (int y = 0; y < quadsY; ++y) {
353 for (int x = 0; x < quadsX; ++x) {
354 uint16_t a = indexOffset + static_cast<uint16_t>(y * (quadsX + 1) + x);
355 uint16_t b = static_cast<uint16_t>(a + quadsX + 1);
356 uint16_t c = static_cast<uint16_t>(b + 1);
357 uint16_t d = static_cast<uint16_t>(a + 1);
358
359 if (!flipWinding) {
360 // First triangle (a, d, b)
361 *indexPtr++ = a;
362 *indexPtr++ = d;
363 *indexPtr++ = b;
364
365 // Second triangle (b, d, c)
366 *indexPtr++ = b;
367 *indexPtr++ = d;
368 *indexPtr++ = c;
369 } else {
370 *indexPtr++ = a;
371 *indexPtr++ = b;
372 *indexPtr++ = d;
373
374 *indexPtr++ = b;
375 *indexPtr++ = c;
376 *indexPtr++ = d;
377 }
378 }
379 }
380 indexOffset += vertexCount;
381 };
382
383 // Generate the 6 faces of the cuboid
384 // Right and Left (YZ plane)
385 uint16_t indexOffset = 0;
386 generatePlane(vertexPtr, indexPtr, indexOffset, zExtent, yExtent, QVector3D(halfXExtent, 0, 0), QVector3D(0, 0, -1), QVector3D(0, 1, 0), QVector3D(1, 0, 0), yzMeshResolution);
387 generatePlane(vertexPtr, indexPtr, indexOffset, zExtent, yExtent, QVector3D(-halfXExtent, 0, 0), QVector3D(0, 0, 1), QVector3D(0, 1, 0), QVector3D(-1, 0, 0), yzMeshResolution);
388
389
390 // Top and Bottom (XZ plane)
391 generatePlane(vertexPtr, indexPtr, indexOffset, xExtent, zExtent, QVector3D(0, halfYExtent, 0), QVector3D(-1, 0, 0), QVector3D(0, 0, 1), QVector3D(0, 1, 0), xzMeshResolution);
392 generatePlane(vertexPtr, indexPtr, indexOffset, xExtent, zExtent, QVector3D(0, -halfYExtent, 0), QVector3D(1, 0, 0), QVector3D(0, 0, 1), QVector3D(0, -1, 0), xzMeshResolution);
393
394 // Front and Back (XY plane)
395 generatePlane(vertexPtr, indexPtr, indexOffset, xExtent, yExtent, QVector3D(0, 0, halfZExtent), QVector3D(1, 0, 0), QVector3D(0, 1, 0), QVector3D(0, 0, 1), xyMeshResolution);
396 generatePlane(vertexPtr, indexPtr, indexOffset, xExtent, yExtent, QVector3D(0, 0, -halfZExtent), QVector3D(-1, 0, 0), QVector3D(0, 1, 0), QVector3D(0, 0, -1), xyMeshResolution);
397
398 // Return the geometry data
399 return geometryData;
400}
401
402#if QT_CONFIG(concurrent)
403void CuboidGeometry::generateCuboidGeometryAsync(QPromise<CuboidGeometry::GeometryData> &promise,
404 float xExtent,
405 float yExtent,
406 float zExtent,
407 QSize yzMeshResolution,
408 QSize xzMeshResolution,
409 QSize xyMeshResolution)
410{
411 auto output = generateCuboidGeometry(xExtent, yExtent, zExtent, yzMeshResolution, xzMeshResolution, xyMeshResolution);
412 promise.addResult(output);
413}
414#endif
415
416QT_END_NAMESPACE
QObject * parent
Definition qobject.h:73
\qmltype Object3D \inqmlmodule QtQuick3D \nativetype QQuick3DObject \inherits QtObject
Combined button and popup list for selecting options.