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 "qtorusgeometry.h"
5#include "qtorusgeometry_p.h"
6
7#include <Qt3DCore/qbuffer.h>
8#include <Qt3DCore/qattribute.h>
9#include <QtGui/QVector3D>
10#include <QtGui/QVector4D>
11
12#include <qmath.h>
13
14QT_BEGIN_NAMESPACE
15
16using namespace Qt3DCore;
17
18namespace Qt3DExtras {
19
20namespace {
21
22int vertexCount(int requestedRings, int requestedSlices)
23{
24 return (requestedRings + 1) * (requestedSlices + 1);
25}
26
27int triangleCount(int requestedRings, int requestedSlices)
28{
29 return 2 * requestedRings * requestedSlices;
30}
31
32QByteArray createTorusVertexData(double radius, double minorRadius,
33 int rings, int slices)
34{
35 // The additional side and ring compared to what the user asked
36 // for is because we also need to ensure proper texture coordinate
37 // wrapping at the seams. Without this the last ring and side would
38 // interpolate the texture coordinates from ~0.9x back down to 0
39 // (the starting value at the first ring). So we insert an extra
40 // ring and side with the same positions as the first ring and side
41 // but with texture coordinates of 1 (compared to 0).
42 const int nVerts = vertexCount(requestedRings: rings, requestedSlices: slices);
43 QByteArray bufferBytes;
44 // vec3 pos, vec2 texCoord, vec3 normal, vec4 tangent
45 const quint32 elementSize = 3 + 2 + 3 + 4;
46 const quint32 stride = elementSize * sizeof(float);
47 bufferBytes.resize(size: stride * nVerts);
48
49 float* fptr = reinterpret_cast<float*>(bufferBytes.data());
50
51 const float ringFactor = (M_PI * 2) / static_cast<float>(rings);
52 const float sliceFactor = (M_PI * 2) / static_cast<float>(slices);
53
54 for (int ring = 0; ring <= rings; ++ring) {
55 const float u = ring * ringFactor;
56 const float cu = qCos(v: u);
57 const float su = qSin(v: u);
58
59 for (int slice = 0; slice <= slices; ++slice) {
60 const float v = slice * sliceFactor;
61 const float cv = qCos(v: v + M_PI);
62 const float sv = qSin(v);
63 const float r = (radius + minorRadius * cv);
64
65 *fptr++ = r * cu;
66 *fptr++ = r * su;
67 *fptr++ = minorRadius * sv;
68
69 *fptr++ = u / (M_PI * 2);
70 *fptr++ = v / (M_PI * 2);
71
72 QVector3D n(cv * cu, cv * su, sv);
73 n.normalize();
74 *fptr++ = n.x();
75 *fptr++ = n.y();
76 *fptr++ = n.z();
77
78 QVector4D t(-su, cu, 0.0f, 1.0f);
79 t.normalize();
80 *fptr++ = t.x();
81 *fptr++ = t.y();
82 *fptr++ = t.z();
83 *fptr++ = t.w();
84 }
85 }
86
87 return bufferBytes;
88}
89
90QByteArray createTorusIndexData(int requestedRings, int requestedSlices)
91{
92 const int slices = requestedSlices + 1;
93 int triangles = triangleCount(requestedRings, requestedSlices);
94 qsizetype indices = triangles * 3;
95 Q_ASSERT(indices < 65536);
96 QByteArray indexBytes;
97 indexBytes.resize(size: indices * sizeof(quint16));
98 quint16* indexPtr = reinterpret_cast<quint16*>(indexBytes.data());
99
100 for (int ring = 0; ring < requestedRings; ++ring) {
101 const int ringStart = ring * slices;
102 const int nextRingStart = (ring + 1) * slices;
103 for (int slice = 0; slice < requestedSlices; ++slice) {
104 const int nextSlice = (slice + 1) % slices;
105 *indexPtr++ = ringStart + slice;
106 *indexPtr++ = ringStart + nextSlice;
107 *indexPtr++ = nextRingStart + slice;
108 *indexPtr++ = ringStart + nextSlice;
109 *indexPtr++ = nextRingStart + nextSlice;
110 *indexPtr++ = nextRingStart + slice;
111 }
112 }
113
114 return indexBytes;
115}
116
117} // anonymous
118
119QTorusGeometryPrivate::QTorusGeometryPrivate()
120 : QGeometryPrivate()
121 , m_rings(16)
122 , m_slices(16)
123 , m_radius(1.0f)
124 , m_minorRadius(1.0f)
125 , m_positionAttribute(nullptr)
126 , m_normalAttribute(nullptr)
127 , m_texCoordAttribute(nullptr)
128 , m_tangentAttribute(nullptr)
129 , m_indexAttribute(nullptr)
130 , m_vertexBuffer(nullptr)
131 , m_indexBuffer(nullptr)
132{
133}
134
135void QTorusGeometryPrivate::init()
136{
137 Q_Q(QTorusGeometry);
138 m_positionAttribute = new QAttribute(q);
139 m_normalAttribute = new QAttribute(q);
140 m_texCoordAttribute = new QAttribute(q);
141 m_tangentAttribute = new QAttribute(q);
142 m_indexAttribute = new QAttribute(q);
143 m_vertexBuffer = new Qt3DCore::QBuffer(q);
144 m_indexBuffer = new Qt3DCore::QBuffer(q);
145 // vec3 pos, vec2 tex, vec3 normal, vec4 tangent
146 const quint32 elementSize = 3 + 2 + 3 + 4;
147 const quint32 stride = elementSize * sizeof(float);
148 const int nVerts = vertexCount(requestedRings: m_rings, requestedSlices: m_slices);
149 const int triangles = triangleCount(requestedRings: m_rings, requestedSlices: m_slices);
150
151 m_positionAttribute->setName(QAttribute::defaultPositionAttributeName());
152 m_positionAttribute->setVertexBaseType(QAttribute::Float);
153 m_positionAttribute->setVertexSize(3);
154 m_positionAttribute->setAttributeType(QAttribute::VertexAttribute);
155 m_positionAttribute->setBuffer(m_vertexBuffer);
156 m_positionAttribute->setByteStride(stride);
157 m_positionAttribute->setCount(nVerts);
158
159 m_texCoordAttribute->setName(QAttribute::defaultTextureCoordinateAttributeName());
160 m_texCoordAttribute->setVertexBaseType(QAttribute::Float);
161 m_texCoordAttribute->setVertexSize(2);
162 m_texCoordAttribute->setAttributeType(QAttribute::VertexAttribute);
163 m_texCoordAttribute->setBuffer(m_vertexBuffer);
164 m_texCoordAttribute->setByteStride(stride);
165 m_texCoordAttribute->setByteOffset(3 * sizeof(float));
166 m_texCoordAttribute->setCount(nVerts);
167
168 m_normalAttribute->setName(QAttribute::defaultNormalAttributeName());
169 m_normalAttribute->setVertexBaseType(QAttribute::Float);
170 m_normalAttribute->setVertexSize(3);
171 m_normalAttribute->setAttributeType(QAttribute::VertexAttribute);
172 m_normalAttribute->setBuffer(m_vertexBuffer);
173 m_normalAttribute->setByteStride(stride);
174 m_normalAttribute->setByteOffset(5 * sizeof(float));
175 m_normalAttribute->setCount(nVerts);
176
177 m_tangentAttribute->setName(QAttribute::defaultTangentAttributeName());
178 m_tangentAttribute->setVertexBaseType(QAttribute::Float);
179 m_tangentAttribute->setVertexSize(4);
180 m_tangentAttribute->setAttributeType(QAttribute::VertexAttribute);
181 m_tangentAttribute->setBuffer(m_vertexBuffer);
182 m_tangentAttribute->setByteStride(stride);
183 m_tangentAttribute->setByteOffset(8 * sizeof(float));
184 m_tangentAttribute->setCount(nVerts);
185
186 m_indexAttribute->setAttributeType(QAttribute::IndexAttribute);
187 m_indexAttribute->setVertexBaseType(QAttribute::UnsignedShort);
188 m_indexAttribute->setBuffer(m_indexBuffer);
189
190 m_indexAttribute->setCount(triangles * 3);
191
192 m_vertexBuffer->setData(generateVertexData());
193 m_indexBuffer->setData(generateIndexData());
194
195 q->addAttribute(attribute: m_positionAttribute);
196 q->addAttribute(attribute: m_texCoordAttribute);
197 q->addAttribute(attribute: m_normalAttribute);
198 q->addAttribute(attribute: m_tangentAttribute);
199 q->addAttribute(attribute: m_indexAttribute);
200}
201
202QByteArray QTorusGeometryPrivate::generateVertexData() const
203{
204 return createTorusVertexData(radius: m_radius, minorRadius: m_minorRadius, rings: m_rings, slices: m_slices);
205}
206
207QByteArray QTorusGeometryPrivate::generateIndexData() const
208{
209 return createTorusIndexData(requestedRings: m_rings, requestedSlices: m_slices);
210}
211
212/*!
213 * \qmltype TorusGeometry
214 * \instantiates Qt3DExtras::QTorusGeometry
215 * \inqmlmodule Qt3D.Extras
216 * \brief TorusGeometry allows creation of a torus in 3D space.
217 *
218 * The TorusGeometry type is most commonly used internally by the TorusMesh type
219 * but can also be used in custom GeometryRenderer types.
220 */
221
222/*!
223 * \qmlproperty int TorusGeometry::rings
224 *
225 * Holds the number of rings in the torus.
226 */
227
228/*!
229 * \qmlproperty int TorusGeometry::slices
230 *
231 * Holds the number of slices in the torus.
232 */
233
234/*!
235 * \qmlproperty real TorusGeometry::radius
236 *
237 * Holds the outer radius of the torus.
238 */
239
240/*!
241 * \qmlproperty real TorusGeometry::minorRadius
242 *
243 * Holds the inner radius of the torus.
244 */
245
246/*!
247 * \qmlproperty Attribute TorusGeometry::positionAttribute
248 *
249 * Holds the geometry position attribute.
250 */
251
252/*!
253 * \qmlproperty Attribute TorusGeometry::normalAttribute
254 *
255 * Holds the geometry normal attribute.
256 */
257
258/*!
259 * \qmlproperty Attribute TorusGeometry::texCoordAttribute
260 *
261 * Holds the geometry texture coordinate attribute.
262 */
263
264/*!
265 * \qmlproperty Attribute TorusGeometry::indexAttribute
266 *
267 * Holds the geometry index attribute.
268 */
269
270/*!
271 * \class Qt3DExtras::QTorusGeometry
272 * \ingroup qt3d-extras-geometries
273 * \inheaderfile Qt3DExtras/QTorusGeometry
274 * \inmodule Qt3DExtras
275 * \brief The QTorusGeometry class allows creation of a torus in 3D space.
276 * \since 5.7
277 * \ingroup geometries
278 * \inherits Qt3DCore::QGeometry
279 *
280 * The QTorusGeometry class is most commonly used internally by the QTorusMesh
281 * but can also be used in custom Qt3DRender::QGeometryRenderer subclasses.
282 */
283
284/*!
285 * Constructs a new QTorusGeometry with \a parent.
286 */
287QTorusGeometry::QTorusGeometry(QNode *parent)
288 : QGeometry(*new QTorusGeometryPrivate(), parent)
289{
290 Q_D(QTorusGeometry);
291 d->init();
292}
293
294/*!
295 * \internal
296 */
297QTorusGeometry::QTorusGeometry(QTorusGeometryPrivate &dd, QNode *parent)
298 : QGeometry(dd, parent)
299{
300 Q_D(QTorusGeometry);
301 d->init();
302}
303
304/*!
305 * \internal
306 */
307QTorusGeometry::~QTorusGeometry()
308{
309}
310
311/*!
312 * Updates vertices based on rings, slices, and radius properties.
313 */
314void QTorusGeometry::updateVertices()
315{
316 Q_D(QTorusGeometry);
317 const int nVerts = vertexCount(requestedRings: d->m_rings, requestedSlices: d->m_slices);
318 d->m_positionAttribute->setCount(nVerts);
319 d->m_texCoordAttribute->setCount(nVerts);
320 d->m_normalAttribute->setCount(nVerts);
321 d->m_vertexBuffer->setData(d->generateVertexData());
322}
323
324/*!
325 * Updates indices based on rings and slices properties.
326 */
327void QTorusGeometry::updateIndices()
328{
329 Q_D(QTorusGeometry);
330 const int triangles = triangleCount(requestedRings: d->m_rings, requestedSlices: d->m_slices);
331 d->m_indexAttribute->setCount(triangles * 3);
332 d->m_indexBuffer->setData(d->generateIndexData());
333}
334
335void QTorusGeometry::setRings(int rings)
336{
337 Q_D(QTorusGeometry);
338 if (rings != d->m_rings) {
339 d->m_rings = rings;
340 updateVertices();
341 updateIndices();
342 emit ringsChanged(rings);
343 }
344}
345
346void QTorusGeometry::setSlices(int slices)
347{
348 Q_D(QTorusGeometry);
349 if (slices != d->m_slices) {
350 d->m_slices = slices;
351 updateVertices();
352 updateIndices();
353 emit slicesChanged(slices);
354 }
355}
356
357void QTorusGeometry::setRadius(float radius)
358{
359 Q_D(QTorusGeometry);
360 if (radius != d->m_radius) {
361 d->m_radius = radius;
362 updateVertices();
363 emit radiusChanged(radius);
364 }
365}
366
367void QTorusGeometry::setMinorRadius(float minorRadius)
368{
369 Q_D(QTorusGeometry);
370 if (minorRadius != d->m_minorRadius) {
371 d->m_minorRadius = minorRadius;
372 updateVertices();
373 emit minorRadiusChanged(minorRadius);
374 }
375}
376
377/*!
378 * \property QTorusGeometry::rings
379 *
380 * Holds the number of rings in the torus.
381 */
382int QTorusGeometry::rings() const
383{
384 Q_D(const QTorusGeometry);
385 return d->m_rings;
386}
387
388/*!
389 * \property QTorusGeometry::slices
390 *
391 * Holds the number of slices in the torus.
392 */
393int QTorusGeometry::slices() const
394{
395 Q_D(const QTorusGeometry);
396 return d->m_slices;
397}
398
399/*!
400 * \property QTorusGeometry::radius
401 *
402 * Holds the outer radius of the torus.
403 */
404float QTorusGeometry::radius() const
405{
406 Q_D(const QTorusGeometry);
407 return d->m_radius;
408}
409
410/*!
411 * \property QTorusGeometry::minorRadius
412 *
413 * Holds the inner radius of the torus.
414 */
415float QTorusGeometry::minorRadius() const
416{
417 Q_D(const QTorusGeometry);
418 return d->m_minorRadius;
419}
420
421/*!
422 * \property QTorusGeometry::positionAttribute
423 *
424 * Holds the geometry position attribute.
425 */
426QAttribute *QTorusGeometry::positionAttribute() const
427{
428 Q_D(const QTorusGeometry);
429 return d->m_positionAttribute;
430}
431
432/*!
433 * \property QTorusGeometry::normalAttribute
434 *
435 * Holds the geometry normal attribute.
436 */
437QAttribute *QTorusGeometry::normalAttribute() const
438{
439 Q_D(const QTorusGeometry);
440 return d->m_normalAttribute;
441}
442
443/*!
444 * \property QTorusGeometry::texCoordAttribute
445 *
446 * Holds the geometry texture coordinate attribute.
447 */
448QAttribute *QTorusGeometry::texCoordAttribute() const
449{
450 Q_D(const QTorusGeometry);
451 return d->m_texCoordAttribute;
452}
453
454/*!
455 * \property QTorusGeometry::indexAttribute
456 *
457 * Holds the geometry index attribute.
458 */
459QAttribute *QTorusGeometry::indexAttribute() const
460{
461 Q_D(const QTorusGeometry);
462 return d->m_indexAttribute;
463}
464
465} // Qt3DExtras
466
467QT_END_NAMESPACE
468
469#include "moc_qtorusgeometry.cpp"
470

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