7#if QT_CONFIG(concurrent)
8#include <QtConcurrentRun>
14
15
16
17
18
19
20
21
22
23
26
27
28
31
32
33
36
37
38
41
42
43
44
47
48
49
50
51
52
53
54
55
56
58SphereGeometry::SphereGeometry(QQuick3DObject *parent)
59 : QQuick3DGeometry(parent)
61#if QT_CONFIG(concurrent)
62 connect(&m_geometryDataWatcher, &QFutureWatcher<GeometryData>::finished,
this, &SphereGeometry::requestFinished);
64 scheduleGeometryUpdate();
67SphereGeometry::~SphereGeometry()
72float SphereGeometry::radius()
const
77void SphereGeometry::setRadius(
float newRadius)
79 if (qFuzzyCompare(m_radius, newRadius))
83 scheduleGeometryUpdate();
86int SphereGeometry::rings()
const
91void SphereGeometry::setRings(
int newRings)
93 if (m_rings == newRings)
97 scheduleGeometryUpdate();
100int SphereGeometry::segments()
const
105void SphereGeometry::setSegments(
int newSegments)
107 if (m_segments == newSegments)
109 m_segments = newSegments;
110 emit segmentsChanged();
111 scheduleGeometryUpdate();
114bool SphereGeometry::asynchronous()
const
116 return m_asynchronous;
119void SphereGeometry::setAsynchronous(
bool newAsynchronous)
121 if (m_asynchronous == newAsynchronous)
123 m_asynchronous = newAsynchronous;
124 emit asynchronousChanged();
127SphereGeometry::Status SphereGeometry::status()
const
132void SphereGeometry::doUpdateGeometry()
135 m_geometryUpdateRequested =
false;
137#if QT_CONFIG(concurrent)
138 if (m_geometryDataFuture.isRunning()) {
139 m_pendingAsyncUpdate =
true;
145 if (m_radius < 0 || m_rings < 1 || m_segments < 3) {
151#if QT_CONFIG(concurrent)
152 if (m_asynchronous) {
153 m_geometryDataFuture = QtConcurrent::run(generateSphereGeometryAsync,
157 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
158 m_status = Status::Loading;
159 Q_EMIT statusChanged();
165 updateGeometry(generateSphereGeometry(m_radius, m_rings, m_segments));
169void SphereGeometry::requestFinished()
171#if QT_CONFIG(concurrent)
172 const auto output = m_geometryDataFuture.takeResult();
173 updateGeometry(output);
177void SphereGeometry::scheduleGeometryUpdate()
179 if (!m_geometryUpdateRequested) {
180 QMetaObject::invokeMethod(
this,
"doUpdateGeometry", Qt::QueuedConnection);
181 m_geometryUpdateRequested =
true;
185void SphereGeometry::updateGeometry(
const GeometryData &geometryData)
187 setStride(
sizeof(
float) * 8);
188 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
189 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
191 QQuick3DGeometry::Attribute::F32Type);
192 addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
194 QQuick3DGeometry::Attribute::F32Type);
195 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
197 QQuick3DGeometry::Attribute::F32Type);
198 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
200 QQuick3DGeometry::Attribute::U16Type);
202 setBounds(geometryData.boundsMin, geometryData.boundsMax);
203 setVertexData(geometryData.vertexData);
204 setIndexData(geometryData.indexData);
208 if (m_pendingAsyncUpdate) {
209 m_pendingAsyncUpdate =
false;
210 scheduleGeometryUpdate();
212 m_status = Status::Ready;
213 Q_EMIT statusChanged();
218SphereGeometry::GeometryData SphereGeometry::generateSphereGeometry(
float radius,
int rings,
int segments)
220 GeometryData geometryData;
223 const int numVertices = (rings + 1) * (segments + 1);
224 const int numIndices = rings * segments * 6;
225 const int vertexStride =
sizeof(
float) * (3 + 2 + 3);
226 const int indexStride =
sizeof(uint16_t);
229 geometryData.vertexData.resize(numVertices * vertexStride);
230 geometryData.indexData.resize(numIndices * indexStride);
234 QVector3D boundsMin(std::numeric_limits<
float>::max(),
235 std::numeric_limits<
float>::max(),
236 std::numeric_limits<
float>::max());
238 QVector3D boundsMax(std::numeric_limits<
float>::lowest(),
239 std::numeric_limits<
float>::lowest(),
240 std::numeric_limits<
float>::lowest());
243 float* vertexPtr =
reinterpret_cast<
float*>(geometryData.vertexData.data());
244 uint16_t* indexPtr =
reinterpret_cast<uint16_t*>(geometryData.indexData.data());
247 for (
int i = 0; i <= rings; ++i) {
248 float phi = M_PI * i / rings;
249 float y = radius * std::cos(phi);
250 float ringRadius = radius * std::sin(phi);
252 for (
int j = 0; j <= segments; ++j) {
253 float theta = 2 * M_PI * j / segments;
254 float x = ringRadius * std::cos(theta);
255 float z = ringRadius * std::sin(theta);
263 *vertexPtr++ = 1.0f -
static_cast<
float>(j) / segments;
264 *vertexPtr++ = 1.0f -
static_cast<
float>(i) / rings;
267 QVector3D normal(x, y, z);
269 *vertexPtr++ = normal.x();
270 *vertexPtr++ = normal.y();
271 *vertexPtr++ = normal.z();
274 boundsMin.setX(std::min(boundsMin.x(), x));
275 boundsMin.setY(std::min(boundsMin.y(), y));
276 boundsMin.setZ(std::min(boundsMin.z(), z));
277 boundsMax.setX(std::max(boundsMax.x(), x));
278 boundsMax.setY(std::max(boundsMax.y(), y));
279 boundsMax.setZ(std::max(boundsMax.z(), z));
284 for (
int i = 0; i < rings; ++i) {
285 for (
int j = 0; j < segments; ++j) {
286 uint16_t a =
static_cast<uint16_t>(i * (segments + 1) + j);
287 uint16_t b =
static_cast<uint16_t>(a + segments + 1);
288 uint16_t c =
static_cast<uint16_t>(b + 1);
289 uint16_t d =
static_cast<uint16_t>(a + 1);
303 geometryData.boundsMin = boundsMin;
304 geometryData.boundsMax = boundsMax;
310#if QT_CONFIG(concurrent)
311void SphereGeometry::generateSphereGeometryAsync(QPromise<SphereGeometry::GeometryData> &promise,
316 auto output = generateSphereGeometry(radius, rings, segments);
317 promise.addResult(output);