9#if QT_CONFIG(concurrent)
10#include <QtConcurrentRun>
16
17
18
19
20
21
22
23
24
25
28
29
30
33
34
35
38
39
40
43
44
45
46
49
50
51
52
53
54
55
56
57
58
60SphereGeometry::SphereGeometry(QQuick3DObject *parent)
61 : QQuick3DGeometry(parent)
63#if QT_CONFIG(concurrent)
64 connect(&m_geometryDataWatcher, &QFutureWatcher<GeometryData>::finished,
this, &SphereGeometry::requestFinished);
66 scheduleGeometryUpdate();
69SphereGeometry::~SphereGeometry()
74float SphereGeometry::radius()
const
79void SphereGeometry::setRadius(
float newRadius)
81 if (qFuzzyCompare(m_radius, newRadius))
85 scheduleGeometryUpdate();
88int SphereGeometry::rings()
const
93void SphereGeometry::setRings(
int newRings)
95 if (m_rings == newRings)
99 scheduleGeometryUpdate();
102int SphereGeometry::segments()
const
107void SphereGeometry::setSegments(
int newSegments)
109 if (m_segments == newSegments)
111 m_segments = newSegments;
112 emit segmentsChanged();
113 scheduleGeometryUpdate();
116bool SphereGeometry::asynchronous()
const
118 return m_asynchronous;
121void SphereGeometry::setAsynchronous(
bool newAsynchronous)
123 if (m_asynchronous == newAsynchronous)
125 m_asynchronous = newAsynchronous;
126 emit asynchronousChanged();
129SphereGeometry::Status SphereGeometry::status()
const
134void SphereGeometry::doUpdateGeometry()
137 m_geometryUpdateRequested =
false;
139#if QT_CONFIG(concurrent)
140 if (m_geometryDataFuture.isRunning()) {
141 m_pendingAsyncUpdate =
true;
147 if (m_radius < 0 || m_rings < 1 || m_segments < 3) {
153#if QT_CONFIG(concurrent)
154 if (m_asynchronous) {
155 m_geometryDataFuture = QtConcurrent::run(generateSphereGeometryAsync,
159 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
160 m_status = Status::Loading;
161 Q_EMIT statusChanged();
167 updateGeometry(generateSphereGeometry(m_radius, m_rings, m_segments));
171void SphereGeometry::requestFinished()
173#if QT_CONFIG(concurrent)
174 const auto output = m_geometryDataFuture.takeResult();
175 updateGeometry(output);
179void SphereGeometry::scheduleGeometryUpdate()
181 if (!m_geometryUpdateRequested) {
182 QMetaObject::invokeMethod(
this,
"doUpdateGeometry", Qt::QueuedConnection);
183 m_geometryUpdateRequested =
true;
187void SphereGeometry::updateGeometry(
const GeometryData &geometryData)
189 setStride(
sizeof(
float) * 8);
190 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
191 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
193 QQuick3DGeometry::Attribute::F32Type);
194 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
196 QQuick3DGeometry::Attribute::F32Type);
197 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
199 QQuick3DGeometry::Attribute::F32Type);
200 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
202 QQuick3DGeometry::Attribute::U16Type);
204 setBounds(geometryData.boundsMin, geometryData.boundsMax);
205 setVertexData(geometryData.vertexData);
206 setIndexData(geometryData.indexData);
210 if (m_pendingAsyncUpdate) {
211 m_pendingAsyncUpdate =
false;
212 scheduleGeometryUpdate();
214 m_status = Status::Ready;
215 Q_EMIT statusChanged();
218 emit geometryChanged();
221SphereGeometry::GeometryData SphereGeometry::generateSphereGeometry(
float radius,
int rings,
int segments)
223 GeometryData geometryData;
226 const int numVertices = (rings + 1) * (segments + 1);
227 const int numIndices = rings * segments * 6;
228 const int vertexStride =
sizeof(
float) * (3 + 2 + 3);
229 const int indexStride =
sizeof(uint16_t);
232 geometryData.vertexData.resize(numVertices * vertexStride);
233 geometryData.indexData.resize(numIndices * indexStride);
237 QVector3D boundsMin(std::numeric_limits<
float>::max(),
238 std::numeric_limits<
float>::max(),
239 std::numeric_limits<
float>::max());
241 QVector3D boundsMax(std::numeric_limits<
float>::lowest(),
242 std::numeric_limits<
float>::lowest(),
243 std::numeric_limits<
float>::lowest());
246 float* vertexPtr =
reinterpret_cast<
float*>(geometryData.vertexData.data());
247 uint16_t* indexPtr =
reinterpret_cast<uint16_t*>(geometryData.indexData.data());
250 for (
int i = 0; i <= rings; ++i) {
251 float phi = M_PI * i / rings;
252 float y = radius * std::cos(phi);
253 float ringRadius = radius * std::sin(phi);
255 for (
int j = 0; j <= segments; ++j) {
256 float theta = 2 * M_PI * j / segments;
257 float x = ringRadius * std::cos(theta);
258 float z = ringRadius * std::sin(theta);
266 *vertexPtr++ = 1.0f -
static_cast<
float>(j) / segments;
267 *vertexPtr++ = 1.0f -
static_cast<
float>(i) / rings;
270 QVector3D normal(x, y, z);
272 *vertexPtr++ = normal.x();
273 *vertexPtr++ = normal.y();
274 *vertexPtr++ = normal.z();
277 boundsMin.setX(std::min(boundsMin.x(), x));
278 boundsMin.setY(std::min(boundsMin.y(), y));
279 boundsMin.setZ(std::min(boundsMin.z(), z));
280 boundsMax.setX(std::max(boundsMax.x(), x));
281 boundsMax.setY(std::max(boundsMax.y(), y));
282 boundsMax.setZ(std::max(boundsMax.z(), z));
287 for (
int i = 0; i < rings; ++i) {
288 for (
int j = 0; j < segments; ++j) {
289 uint16_t a =
static_cast<uint16_t>(i * (segments + 1) + j);
290 uint16_t b =
static_cast<uint16_t>(a + segments + 1);
291 uint16_t c =
static_cast<uint16_t>(b + 1);
292 uint16_t d =
static_cast<uint16_t>(a + 1);
306 geometryData.boundsMin = boundsMin;
307 geometryData.boundsMax = boundsMax;
313#if QT_CONFIG(concurrent)
314void SphereGeometry::generateSphereGeometryAsync(QPromise<SphereGeometry::GeometryData> &promise,
319 auto output = generateSphereGeometry(radius, rings, segments);
320 promise.addResult(output);