1// Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qspheregeometry.h"
5#include "qspheregeometry_p.h"
6
7#include <Qt3DCore/qbuffer.h>
8#include <Qt3DCore/qattribute.h>
9
10#include <qmath.h>
11
12#ifndef _USE_MATH_DEFINES
13# define _USE_MATH_DEFINES // For MSVC
14#endif
15
16
17QT_BEGIN_NAMESPACE
18
19
20namespace Qt3DExtras {
21
22using namespace Qt3DCore;
23
24namespace {
25
26QByteArray createSphereMeshVertexData(float radius, int rings, int slices)
27{
28 QByteArray bufferBytes;
29 // vec3 pos, vec2 texCoord, vec3 normal, vec4 tangent
30 const quint32 elementSize = 3 + 2 + 3 + 4;
31 const quint32 stride = elementSize * sizeof(float);
32 const int nVerts = (slices + 1) * (rings + 1);
33 bufferBytes.resize(size: stride * nVerts);
34
35 float* fptr = reinterpret_cast<float*>(bufferBytes.data());
36
37 const float dTheta = (M_PI * 2) / static_cast<float>( slices );
38 const float dPhi = M_PI / static_cast<float>( rings );
39 const float du = 1.0f / static_cast<float>( slices );
40 const float dv = 1.0f / static_cast<float>( rings );
41
42 // Iterate over latitudes (rings)
43 for ( int lat = 0; lat < rings + 1; ++lat )
44 {
45 const float phi = M_PI_2 - static_cast<float>( lat ) * dPhi;
46 const float cosPhi = qCos( v: phi );
47 const float sinPhi = qSin( v: phi );
48 const float v = 1.0f - static_cast<float>( lat ) * dv;
49
50 // Iterate over longitudes (slices)
51 for ( int lon = 0; lon < slices + 1; ++lon )
52 {
53 const float theta = static_cast<float>( lon ) * dTheta;
54 const float cosTheta = qCos( v: theta );
55 const float sinTheta = qSin( v: theta );
56 const float u = static_cast<float>( lon ) * du;
57
58 *fptr++ = radius * cosTheta * cosPhi;
59 *fptr++ = radius * sinPhi;
60 *fptr++ = radius * sinTheta * cosPhi;
61
62 *fptr++ = u;
63 *fptr++ = v;
64
65 *fptr++ = cosTheta * cosPhi;
66 *fptr++ = sinPhi;
67 *fptr++ = sinTheta * cosPhi;
68
69 *fptr++ = sinTheta;
70 *fptr++ = 0.0;
71 *fptr++ = -cosTheta;
72 *fptr++ = 1.0;
73 }
74 }
75 return bufferBytes;
76}
77
78QByteArray createSphereMeshIndexData(int rings, int slices)
79{
80 int faces = (slices * 2) * (rings - 2); // two tris per slice, for all middle rings
81 faces += 2 * slices; // tri per slice for both top and bottom
82
83 QByteArray indexBytes;
84 const qsizetype indices = faces * 3;
85 Q_ASSERT(indices < 65536);
86 indexBytes.resize(size: indices * sizeof(quint16));
87 quint16 *indexPtr = reinterpret_cast<quint16*>(indexBytes.data());
88
89 // top cap
90 {
91 const int nextRingStartIndex = slices + 1;
92 for ( int j = 0; j < slices; ++j )
93 {
94 *indexPtr++ = nextRingStartIndex + j;
95 *indexPtr++ = 0;
96 *indexPtr++ = nextRingStartIndex + j + 1;
97 }
98 }
99
100 for ( int i = 1; i < (rings - 1); ++i )
101 {
102 const int ringStartIndex = i * ( slices + 1 );
103 const int nextRingStartIndex = ( i + 1 ) * ( slices + 1 );
104
105 for ( int j = 0; j < slices; ++j )
106 {
107 // Split the quad into two triangles
108 *indexPtr++ = ringStartIndex + j;
109 *indexPtr++ = ringStartIndex + j + 1;
110 *indexPtr++ = nextRingStartIndex + j;
111 *indexPtr++ = nextRingStartIndex + j;
112 *indexPtr++ = ringStartIndex + j + 1;
113 *indexPtr++ = nextRingStartIndex + j + 1;
114 }
115 }
116
117 // bottom cap
118 {
119 const int ringStartIndex = (rings - 1) * ( slices + 1);
120 const int nextRingStartIndex = (rings) * ( slices + 1);
121 for ( int j = 0; j < slices; ++j )
122 {
123 *indexPtr++ = ringStartIndex + j + 1;
124 *indexPtr++ = nextRingStartIndex;
125 *indexPtr++ = ringStartIndex + j;
126 }
127 }
128
129 return indexBytes;
130}
131
132} // anonymous
133
134QSphereGeometryPrivate::QSphereGeometryPrivate()
135 : QGeometryPrivate()
136 , m_generateTangents(false)
137 , m_rings(16)
138 , m_slices(16)
139 , m_radius(1.0f)
140 , m_positionAttribute(nullptr)
141 , m_normalAttribute(nullptr)
142 , m_texCoordAttribute(nullptr)
143 , m_tangentAttribute(nullptr)
144 , m_indexAttribute(nullptr)
145 , m_vertexBuffer(nullptr)
146 , m_indexBuffer(nullptr)
147{
148}
149
150void QSphereGeometryPrivate::init()
151{
152 Q_Q(QSphereGeometry);
153 m_positionAttribute = new QAttribute(q);
154 m_normalAttribute = new QAttribute(q);
155 m_texCoordAttribute = new QAttribute(q);
156 m_tangentAttribute = new QAttribute(q);
157 m_indexAttribute = new QAttribute(q);
158 m_vertexBuffer = new Qt3DCore::QBuffer(q);
159 m_indexBuffer = new Qt3DCore::QBuffer(q);
160
161 // vec3 pos, vec2 tex, vec3 normal, vec4 tangent
162 const quint32 elementSize = 3 + 2 + 3 + 4;
163 const quint32 stride = elementSize * sizeof(float);
164 const int nVerts = (m_slices + 1) * (m_rings + 1);
165 const int faces = (m_slices * 2) * (m_rings - 2) + (2 * m_slices);
166
167 m_positionAttribute->setName(QAttribute::defaultPositionAttributeName());
168 m_positionAttribute->setVertexBaseType(QAttribute::Float);
169 m_positionAttribute->setVertexSize(3);
170 m_positionAttribute->setAttributeType(QAttribute::VertexAttribute);
171 m_positionAttribute->setBuffer(m_vertexBuffer);
172 m_positionAttribute->setByteStride(stride);
173 m_positionAttribute->setCount(nVerts);
174
175 m_texCoordAttribute->setName(QAttribute::defaultTextureCoordinateAttributeName());
176 m_texCoordAttribute->setVertexBaseType(QAttribute::Float);
177 m_texCoordAttribute->setVertexSize(2);
178 m_texCoordAttribute->setAttributeType(QAttribute::VertexAttribute);
179 m_texCoordAttribute->setBuffer(m_vertexBuffer);
180 m_texCoordAttribute->setByteStride(stride);
181 m_texCoordAttribute->setByteOffset(3 * sizeof(float));
182 m_texCoordAttribute->setCount(nVerts);
183
184 m_normalAttribute->setName(QAttribute::defaultNormalAttributeName());
185 m_normalAttribute->setVertexBaseType(QAttribute::Float);
186 m_normalAttribute->setVertexSize(3);
187 m_normalAttribute->setAttributeType(QAttribute::VertexAttribute);
188 m_normalAttribute->setBuffer(m_vertexBuffer);
189 m_normalAttribute->setByteStride(stride);
190 m_normalAttribute->setByteOffset(5 * sizeof(float));
191 m_normalAttribute->setCount(nVerts);
192
193 m_tangentAttribute->setName(QAttribute::defaultTangentAttributeName());
194 m_tangentAttribute->setVertexBaseType(QAttribute::Float);
195 m_tangentAttribute->setVertexSize(4);
196 m_tangentAttribute->setAttributeType(QAttribute::VertexAttribute);
197 m_tangentAttribute->setBuffer(m_vertexBuffer);
198 m_tangentAttribute->setByteStride(stride);
199 m_tangentAttribute->setByteOffset(8 * sizeof(float));
200 m_tangentAttribute->setCount(nVerts);
201
202 m_indexAttribute->setAttributeType(QAttribute::IndexAttribute);
203 m_indexAttribute->setVertexBaseType(QAttribute::UnsignedShort);
204 m_indexAttribute->setBuffer(m_indexBuffer);
205
206 m_indexAttribute->setCount(faces * 3);
207
208 m_vertexBuffer->setData(generateVertexData());
209 m_indexBuffer->setData(generateIndexData());
210
211 q->addAttribute(attribute: m_positionAttribute);
212 q->addAttribute(attribute: m_texCoordAttribute);
213 q->addAttribute(attribute: m_normalAttribute);
214 if (m_generateTangents)
215 q->addAttribute(attribute: m_tangentAttribute);
216 q->addAttribute(attribute: m_indexAttribute);
217}
218
219QByteArray QSphereGeometryPrivate::generateVertexData() const
220{
221 return createSphereMeshVertexData(radius: m_radius, rings: m_rings, slices: m_slices);
222}
223
224QByteArray QSphereGeometryPrivate::generateIndexData() const
225{
226 return createSphereMeshIndexData(rings: m_rings, slices: m_slices);
227}
228
229/*!
230 * \qmltype SphereGeometry
231 * \nativetype Qt3DExtras::QSphereGeometry
232 * \inqmlmodule Qt3D.Extras
233 * \brief SphereGeometry allows creation of a sphere in 3D space.
234 *
235 * The SphereGeometry type is most commonly used internally by the SphereMesh type
236 * but can also be used in custom GeometryRenderer types.
237 */
238
239/*!
240 * \qmlproperty int SphereGeometry::rings
241 *
242 * Holds the number of rings in the sphere.
243 */
244
245/*!
246 * \qmlproperty int SphereGeometry::slices
247 *
248 * Holds the number of slices in the sphere.
249 */
250
251/*!
252 * \qmlproperty real SphereGeometry::radius
253 *
254 * Holds the radius of the sphere.
255 */
256
257/*!
258 * \qmlproperty bool SphereGeometry::generateTangents
259 *
260 * Holds the value of the automatic tangent vectors generation flag.
261 * Tangent vectors are orthogonal to normal vectors.
262 */
263
264/*!
265 * \qmlproperty Attribute SphereGeometry::positionAttribute
266 *
267 * Holds the geometry position attribute.
268 */
269
270/*!
271 * \qmlproperty Attribute SphereGeometry::normalAttribute
272 *
273 * Holds the geometry normal attribute.
274 */
275
276/*!
277 * \qmlproperty Attribute SphereGeometry::texCoordAttribute
278 *
279 * Holds the geometry texture coordinate attribute.
280 */
281
282/*!
283 * \qmlproperty Attribute SphereGeometry::tangentAttribute
284 *
285 * Holds the geometry tangent attribute.
286 */
287
288/*!
289 * \qmlproperty Attribute SphereGeometry::indexAttribute
290 *
291 * Holds the geometry index attribute.
292 */
293
294/*!
295 * \class Qt3DExtras::QSphereGeometry
296 * \ingroup qt3d-extras-geometries
297 * \inheaderfile Qt3DExtras/QSphereGeometry
298 * \inmodule Qt3DExtras
299 * \brief The QSphereGeometry class allows creation of a sphere in 3D space.
300 * \since 5.7
301 * \ingroup geometries
302 * \inherits Qt3DCore::QGeometry
303 *
304 * The QSphereGeometry class is most commonly used internally by the QSphereMesh
305 * but can also be used in custom Qt3DRender::QGeometryRenderer subclasses.
306 */
307
308/*!
309 * Constructs a new QSphereGeometry with \a parent.
310 */
311QSphereGeometry::QSphereGeometry(QNode *parent)
312 : QGeometry(*new QSphereGeometryPrivate(), parent)
313{
314 Q_D(QSphereGeometry);
315 d->init();
316}
317
318/*!
319 * \internal
320 */
321QSphereGeometry::QSphereGeometry(QSphereGeometryPrivate &dd, QNode *parent)
322 : QGeometry(dd, parent)
323{
324 Q_D(QSphereGeometry);
325 d->init();
326}
327
328/*!
329 * \internal
330 */
331QSphereGeometry::~QSphereGeometry()
332{
333}
334
335/*!
336 * Updates vertices based on rings, slices, and radius properties
337 */
338void QSphereGeometry::updateVertices()
339{
340 Q_D(QSphereGeometry);
341 const int nVerts = (d->m_slices + 1) * (d->m_rings + 1);
342 d->m_positionAttribute->setCount(nVerts);
343 d->m_texCoordAttribute->setCount(nVerts);
344 d->m_normalAttribute->setCount(nVerts);
345 d->m_tangentAttribute->setCount(nVerts);
346 d->m_vertexBuffer->setData(d->generateVertexData());
347}
348
349/*!
350 * Updates indices based on rings and slices properties.
351 */
352void QSphereGeometry::updateIndices()
353{
354 Q_D(QSphereGeometry);
355 const int faces = (d->m_slices * 2) * (d->m_rings - 2) + (2 * d->m_slices);
356 d->m_indexAttribute->setCount(faces * 3);
357 d->m_indexBuffer->setData(d->generateIndexData());
358}
359
360void QSphereGeometry::setRings(int rings)
361{
362 Q_D(QSphereGeometry);
363 if (rings != d->m_rings) {
364 d->m_rings = rings;
365 updateVertices();
366 updateIndices();
367 emit ringsChanged(rings);
368 }
369}
370
371void QSphereGeometry::setSlices(int slices)
372{
373 Q_D(QSphereGeometry);
374 if (slices != d->m_slices) {
375 d->m_slices = slices;
376 updateVertices();
377 updateIndices();
378 emit slicesChanged(slices);
379 }
380}
381
382void QSphereGeometry::setRadius(float radius)
383{
384 Q_D(QSphereGeometry);
385 if (radius != d->m_radius) {
386 d->m_radius = radius;
387 updateVertices();
388 emit radiusChanged(radius);
389 }
390}
391
392void QSphereGeometry::setGenerateTangents(bool gen)
393{
394 Q_D(QSphereGeometry);
395 if (d->m_generateTangents != gen) {
396 if (d->m_generateTangents)
397 removeAttribute(attribute: d->m_tangentAttribute);
398 d->m_generateTangents = gen;
399 if (d->m_generateTangents)
400 addAttribute(attribute: d->m_tangentAttribute);
401 emit generateTangentsChanged(generateTangents: gen);
402 }
403}
404
405/*!
406 * \property Qt3DExtras::QSphereGeometry::generateTangents
407 *
408 * Holds the value of the automatic tangent vectors generation flag.
409 * Tangent vectors are orthogonal to normal vectors.
410 */
411bool QSphereGeometry::generateTangents() const
412{
413 Q_D(const QSphereGeometry);
414 return d->m_generateTangents;
415}
416
417/*!
418 * \property Qt3DExtras::QSphereGeometry::rings
419 *
420 * Holds the number of rings in the sphere.
421 */
422int QSphereGeometry::rings() const
423{
424 Q_D(const QSphereGeometry);
425 return d->m_rings;
426}
427
428/*!
429 * \property Qt3DExtras::QSphereGeometry::slices
430 *
431 * Holds the number of slices in the sphere.
432 */
433int QSphereGeometry::slices() const
434{
435 Q_D(const QSphereGeometry);
436 return d->m_slices;
437}
438
439/*!
440 * \property Qt3DExtras::QSphereGeometry::radius
441 *
442 * Holds the radius of the sphere.
443 */
444float QSphereGeometry::radius() const
445{
446 Q_D(const QSphereGeometry);
447 return d->m_radius;
448}
449
450/*!
451 * \property Qt3DExtras::QSphereGeometry::positionAttribute
452 *
453 * Holds the geometry position attribute.
454 */
455QAttribute *QSphereGeometry::positionAttribute() const
456{
457 Q_D(const QSphereGeometry);
458 return d->m_positionAttribute;
459}
460
461/*!
462 * \property Qt3DExtras::QSphereGeometry::normalAttribute
463 *
464 * Holds the geometry normal attribute.
465 */
466QAttribute *QSphereGeometry::normalAttribute() const
467{
468 Q_D(const QSphereGeometry);
469 return d->m_normalAttribute;
470}
471
472/*!
473 * \property Qt3DExtras::QSphereGeometry::texCoordAttribute
474 *
475 * Holds the geometry texture coordinate attribute.
476 */
477QAttribute *QSphereGeometry::texCoordAttribute() const
478{
479 Q_D(const QSphereGeometry);
480 return d->m_texCoordAttribute;
481}
482
483/*!
484 * \property Qt3DExtras::QSphereGeometry::tangentAttribute
485 *
486 * Holds the geometry tangent attribute.
487 */
488QAttribute *QSphereGeometry::tangentAttribute() const
489{
490 Q_D(const QSphereGeometry);
491 return d->m_tangentAttribute;
492}
493
494/*!
495 * \property Qt3DExtras::QSphereGeometry::indexAttribute
496 *
497 * Holds the geometry index attribute.
498 */
499QAttribute *QSphereGeometry::indexAttribute() const
500{
501 Q_D(const QSphereGeometry);
502 return d->m_indexAttribute;
503}
504
505} // Qt3DExtras
506
507QT_END_NAMESPACE
508
509#include "moc_qspheregeometry.cpp"
510
511

source code of qt3d/src/extras/geometries/qspheregeometry.cpp