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

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