1/****************************************************************************
2**
3** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qtorusgeometry.h"
41#include "qtorusgeometry_p.h"
42
43#include <Qt3DRender/qbuffer.h>
44#include <Qt3DRender/qbufferdatagenerator.h>
45#include <Qt3DRender/qattribute.h>
46#include <QtGui/QVector3D>
47#include <QtGui/QVector4D>
48
49#include <qmath.h>
50
51QT_BEGIN_NAMESPACE
52
53using namespace Qt3DRender;
54
55namespace Qt3DExtras {
56
57namespace {
58
59int vertexCount(int requestedRings, int requestedSlices)
60{
61 return (requestedRings + 1) * (requestedSlices + 1);
62}
63
64int triangleCount(int requestedRings, int requestedSlices)
65{
66 return 2 * requestedRings * requestedSlices;
67}
68
69QByteArray createTorusVertexData(double radius, double minorRadius,
70 int rings, int slices)
71{
72 // The additional side and ring compared to what the user asked
73 // for is because we also need to ensure proper texture coordinate
74 // wrapping at the seams. Without this the last ring and side would
75 // interpolate the texture coordinates from ~0.9x back down to 0
76 // (the starting value at the first ring). So we insert an extra
77 // ring and side with the same positions as the first ring and side
78 // but with texture coordinates of 1 (compared to 0).
79 const int nVerts = vertexCount(requestedRings: rings, requestedSlices: slices);
80 QByteArray bufferBytes;
81 // vec3 pos, vec2 texCoord, vec3 normal, vec4 tangent
82 const quint32 elementSize = 3 + 2 + 3 + 4;
83 const quint32 stride = elementSize * sizeof(float);
84 bufferBytes.resize(size: stride * nVerts);
85
86 float* fptr = reinterpret_cast<float*>(bufferBytes.data());
87
88 const float ringFactor = (M_PI * 2) / static_cast<float>(rings);
89 const float sliceFactor = (M_PI * 2) / static_cast<float>(slices);
90
91 for (int ring = 0; ring <= rings; ++ring) {
92 const float u = ring * ringFactor;
93 const float cu = qCos(v: u);
94 const float su = qSin(v: u);
95
96 for (int slice = 0; slice <= slices; ++slice) {
97 const float v = slice * sliceFactor;
98 const float cv = qCos(v: v + M_PI);
99 const float sv = qSin(v);
100 const float r = (radius + minorRadius * cv);
101
102 *fptr++ = r * cu;
103 *fptr++ = r * su;
104 *fptr++ = minorRadius * sv;
105
106 *fptr++ = u / (M_PI * 2);
107 *fptr++ = v / (M_PI * 2);
108
109 QVector3D n(cv * cu, cv * su, sv);
110 n.normalize();
111 *fptr++ = n.x();
112 *fptr++ = n.y();
113 *fptr++ = n.z();
114
115 QVector4D t(-su, cu, 0.0f, 1.0f);
116 t.normalize();
117 *fptr++ = t.x();
118 *fptr++ = t.y();
119 *fptr++ = t.z();
120 *fptr++ = t.w();
121 }
122 }
123
124 return bufferBytes;
125}
126
127QByteArray createTorusIndexData(int requestedRings, int requestedSlices)
128{
129 const int slices = requestedSlices + 1;
130 int triangles = triangleCount(requestedRings, requestedSlices);
131 int indices = triangles * 3;
132 Q_ASSERT(indices < 65536);
133 QByteArray indexBytes;
134 indexBytes.resize(size: indices * sizeof(quint16));
135 quint16* indexPtr = reinterpret_cast<quint16*>(indexBytes.data());
136
137 for (int ring = 0; ring < requestedRings; ++ring) {
138 const int ringStart = ring * slices;
139 const int nextRingStart = (ring + 1) * slices;
140 for (int slice = 0; slice < requestedSlices; ++slice) {
141 const int nextSlice = (slice + 1) % slices;
142 *indexPtr++ = ringStart + slice;
143 *indexPtr++ = ringStart + nextSlice;
144 *indexPtr++ = nextRingStart + slice;
145 *indexPtr++ = ringStart + nextSlice;
146 *indexPtr++ = nextRingStart + nextSlice;
147 *indexPtr++ = nextRingStart + slice;
148 }
149 }
150
151 return indexBytes;
152}
153
154} // anonymous
155
156class TorusVertexDataFunctor : public QBufferDataGenerator
157{
158public:
159 TorusVertexDataFunctor(int rings, int slices, float radius, float minorRadius)
160 : m_rings(rings)
161 , m_slices(slices)
162 , m_radius(radius)
163 , m_minorRadius(minorRadius)
164 {
165 }
166
167 QByteArray operator ()() override
168 {
169 return createTorusVertexData(radius: m_radius, minorRadius: m_minorRadius, rings: m_rings, slices: m_slices);
170 }
171
172 bool operator ==(const QBufferDataGenerator &other) const override
173 {
174 const TorusVertexDataFunctor *otherFunctor = functor_cast<TorusVertexDataFunctor>(other: &other);
175 if (otherFunctor != nullptr)
176 return (otherFunctor->m_rings == m_rings &&
177 otherFunctor->m_slices == m_slices &&
178 otherFunctor->m_radius == m_radius &&
179 otherFunctor->m_minorRadius == m_minorRadius);
180 return false;
181 }
182
183 QT3D_FUNCTOR(TorusVertexDataFunctor)
184
185private:
186 int m_rings;
187 int m_slices;
188 float m_radius;
189 float m_minorRadius;
190};
191
192class TorusIndexDataFunctor : public QBufferDataGenerator
193{
194public:
195 TorusIndexDataFunctor(int rings, int slices)
196 : m_rings(rings)
197 , m_slices(slices)
198 {
199 }
200
201 QByteArray operator ()() override
202 {
203 return createTorusIndexData(requestedRings: m_rings, requestedSlices: m_slices);
204 }
205
206 bool operator ==(const QBufferDataGenerator &other) const override
207 {
208 const TorusIndexDataFunctor *otherFunctor = functor_cast<TorusIndexDataFunctor>(other: &other);
209 if (otherFunctor != nullptr)
210 return (otherFunctor->m_rings == m_rings &&
211 otherFunctor->m_slices == m_slices);
212 return false;
213 }
214
215 QT3D_FUNCTOR(TorusIndexDataFunctor)
216
217private:
218 int m_rings;
219 int m_slices;
220};
221
222QTorusGeometryPrivate::QTorusGeometryPrivate()
223 : QGeometryPrivate()
224 , m_rings(16)
225 , m_slices(16)
226 , m_radius(1.0f)
227 , m_minorRadius(1.0f)
228 , m_positionAttribute(nullptr)
229 , m_normalAttribute(nullptr)
230 , m_texCoordAttribute(nullptr)
231 , m_tangentAttribute(nullptr)
232 , m_indexAttribute(nullptr)
233 , m_vertexBuffer(nullptr)
234 , m_indexBuffer(nullptr)
235{
236}
237
238void QTorusGeometryPrivate::init()
239{
240 Q_Q(QTorusGeometry);
241 m_positionAttribute = new QAttribute(q);
242 m_normalAttribute = new QAttribute(q);
243 m_texCoordAttribute = new QAttribute(q);
244 m_tangentAttribute = new QAttribute(q);
245 m_indexAttribute = new QAttribute(q);
246 m_vertexBuffer = new Qt3DRender::QBuffer(q);
247 m_indexBuffer = new Qt3DRender::QBuffer(q);
248 // vec3 pos, vec2 tex, vec3 normal, vec4 tangent
249 const quint32 elementSize = 3 + 2 + 3 + 4;
250 const quint32 stride = elementSize * sizeof(float);
251 const int nVerts = vertexCount(requestedRings: m_rings, requestedSlices: m_slices);
252 const int triangles = triangleCount(requestedRings: m_rings, requestedSlices: m_slices);
253
254 m_positionAttribute->setName(QAttribute::defaultPositionAttributeName());
255 m_positionAttribute->setVertexBaseType(QAttribute::Float);
256 m_positionAttribute->setVertexSize(3);
257 m_positionAttribute->setAttributeType(QAttribute::VertexAttribute);
258 m_positionAttribute->setBuffer(m_vertexBuffer);
259 m_positionAttribute->setByteStride(stride);
260 m_positionAttribute->setCount(nVerts);
261
262 m_texCoordAttribute->setName(QAttribute::defaultTextureCoordinateAttributeName());
263 m_texCoordAttribute->setVertexBaseType(QAttribute::Float);
264 m_texCoordAttribute->setVertexSize(2);
265 m_texCoordAttribute->setAttributeType(QAttribute::VertexAttribute);
266 m_texCoordAttribute->setBuffer(m_vertexBuffer);
267 m_texCoordAttribute->setByteStride(stride);
268 m_texCoordAttribute->setByteOffset(3 * sizeof(float));
269 m_texCoordAttribute->setCount(nVerts);
270
271 m_normalAttribute->setName(QAttribute::defaultNormalAttributeName());
272 m_normalAttribute->setVertexBaseType(QAttribute::Float);
273 m_normalAttribute->setVertexSize(3);
274 m_normalAttribute->setAttributeType(QAttribute::VertexAttribute);
275 m_normalAttribute->setBuffer(m_vertexBuffer);
276 m_normalAttribute->setByteStride(stride);
277 m_normalAttribute->setByteOffset(5 * sizeof(float));
278 m_normalAttribute->setCount(nVerts);
279
280 m_tangentAttribute->setName(QAttribute::defaultTangentAttributeName());
281 m_tangentAttribute->setVertexBaseType(QAttribute::Float);
282 m_tangentAttribute->setVertexSize(4);
283 m_tangentAttribute->setAttributeType(QAttribute::VertexAttribute);
284 m_tangentAttribute->setBuffer(m_vertexBuffer);
285 m_tangentAttribute->setByteStride(stride);
286 m_tangentAttribute->setByteOffset(8 * sizeof(float));
287 m_tangentAttribute->setCount(nVerts);
288
289 m_indexAttribute->setAttributeType(QAttribute::IndexAttribute);
290 m_indexAttribute->setVertexBaseType(QAttribute::UnsignedShort);
291 m_indexAttribute->setBuffer(m_indexBuffer);
292
293 m_indexAttribute->setCount(triangles * 3);
294
295 m_vertexBuffer->setDataGenerator(QSharedPointer<TorusVertexDataFunctor>::create(arguments&: m_rings, arguments&: m_slices, arguments&: m_radius, arguments&: m_minorRadius));
296 m_indexBuffer->setDataGenerator(QSharedPointer<TorusIndexDataFunctor>::create(arguments&: m_rings, arguments&: m_slices));
297
298 q->addAttribute(attribute: m_positionAttribute);
299 q->addAttribute(attribute: m_texCoordAttribute);
300 q->addAttribute(attribute: m_normalAttribute);
301 q->addAttribute(attribute: m_tangentAttribute);
302 q->addAttribute(attribute: m_indexAttribute);
303}
304
305/*!
306 * \qmltype TorusGeometry
307 * \instantiates Qt3DExtras::QTorusGeometry
308 * \inqmlmodule Qt3D.Extras
309 * \brief TorusGeometry allows creation of a torus in 3D space.
310 *
311 * The TorusGeometry type is most commonly used internally by the TorusMesh type
312 * but can also be used in custom GeometryRenderer types.
313 */
314
315/*!
316 * \qmlproperty int TorusGeometry::rings
317 *
318 * Holds the number of rings in the torus.
319 */
320
321/*!
322 * \qmlproperty int TorusGeometry::slices
323 *
324 * Holds the number of slices in the torus.
325 */
326
327/*!
328 * \qmlproperty real TorusGeometry::radius
329 *
330 * Holds the outer radius of the torus.
331 */
332
333/*!
334 * \qmlproperty real TorusGeometry::minorRadius
335 *
336 * Holds the inner radius of the torus.
337 */
338
339/*!
340 * \qmlproperty Attribute TorusGeometry::positionAttribute
341 *
342 * Holds the geometry position attribute.
343 */
344
345/*!
346 * \qmlproperty Attribute TorusGeometry::normalAttribute
347 *
348 * Holds the geometry normal attribute.
349 */
350
351/*!
352 * \qmlproperty Attribute TorusGeometry::texCoordAttribute
353 *
354 * Holds the geometry texture coordinate attribute.
355 */
356
357/*!
358 * \qmlproperty Attribute TorusGeometry::indexAttribute
359 *
360 * Holds the geometry index attribute.
361 */
362
363/*!
364 * \class Qt3DExtras::QTorusGeometry
365 \ingroup qt3d-extras-geometries
366 * \inheaderfile Qt3DExtras/QTorusGeometry
367 * \inmodule Qt3DExtras
368 * \brief The QTorusGeometry class allows creation of a torus in 3D space.
369 * \since 5.7
370 * \ingroup geometries
371 * \inherits Qt3DRender::QGeometry
372 *
373 * The QTorusGeometry class is most commonly used internally by the QTorusMesh
374 * but can also be used in custom Qt3DRender::QGeometryRenderer subclasses.
375 */
376
377/*!
378 * Constructs a new QTorusGeometry with \a parent.
379 */
380QTorusGeometry::QTorusGeometry(QNode *parent)
381 : QGeometry(*new QTorusGeometryPrivate(), parent)
382{
383 Q_D(QTorusGeometry);
384 d->init();
385}
386
387/*!
388 * \internal
389 */
390QTorusGeometry::QTorusGeometry(QTorusGeometryPrivate &dd, QNode *parent)
391 : QGeometry(dd, parent)
392{
393 Q_D(QTorusGeometry);
394 d->init();
395}
396
397/*!
398 * \internal
399 */
400QTorusGeometry::~QTorusGeometry()
401{
402}
403
404/*!
405 * Updates vertices based on rings, slices, and radius properties.
406 */
407void QTorusGeometry::updateVertices()
408{
409 Q_D(QTorusGeometry);
410 const int nVerts = vertexCount(requestedRings: d->m_rings, requestedSlices: d->m_slices);
411 d->m_positionAttribute->setCount(nVerts);
412 d->m_texCoordAttribute->setCount(nVerts);
413 d->m_normalAttribute->setCount(nVerts);
414 d->m_vertexBuffer->setDataGenerator(QSharedPointer<TorusVertexDataFunctor>::create(arguments&: d->m_rings, arguments&: d->m_slices, arguments&: d->m_radius, arguments&: d->m_minorRadius));
415}
416
417/*!
418 * Updates indices based on rings and slices properties.
419 */
420void QTorusGeometry::updateIndices()
421{
422 Q_D(QTorusGeometry);
423 const int triangles = triangleCount(requestedRings: d->m_rings, requestedSlices: d->m_slices);
424 d->m_indexAttribute->setCount(triangles * 3);
425 d->m_indexBuffer->setDataGenerator(QSharedPointer<TorusIndexDataFunctor>::create(arguments&: d->m_rings, arguments&: d->m_slices));
426}
427
428void QTorusGeometry::setRings(int rings)
429{
430 Q_D(QTorusGeometry);
431 if (rings != d->m_rings) {
432 d->m_rings = rings;
433 updateVertices();
434 updateIndices();
435 emit ringsChanged(rings);
436 }
437}
438
439void QTorusGeometry::setSlices(int slices)
440{
441 Q_D(QTorusGeometry);
442 if (slices != d->m_slices) {
443 d->m_slices = slices;
444 updateVertices();
445 updateIndices();
446 emit slicesChanged(slices);
447 }
448}
449
450void QTorusGeometry::setRadius(float radius)
451{
452 Q_D(QTorusGeometry);
453 if (radius != d->m_radius) {
454 d->m_radius = radius;
455 updateVertices();
456 emit radiusChanged(radius);
457 }
458}
459
460void QTorusGeometry::setMinorRadius(float minorRadius)
461{
462 Q_D(QTorusGeometry);
463 if (minorRadius != d->m_minorRadius) {
464 d->m_minorRadius = minorRadius;
465 updateVertices();
466 emit minorRadiusChanged(minorRadius);
467 }
468}
469
470/*!
471 * \property QTorusGeometry::rings
472 *
473 * Holds the number of rings in the torus.
474 */
475int QTorusGeometry::rings() const
476{
477 Q_D(const QTorusGeometry);
478 return d->m_rings;
479}
480
481/*!
482 * \property QTorusGeometry::slices
483 *
484 * Holds the number of slices in the torus.
485 */
486int QTorusGeometry::slices() const
487{
488 Q_D(const QTorusGeometry);
489 return d->m_slices;
490}
491
492/*!
493 * \property QTorusGeometry::radius
494 *
495 * Holds the outer radius of the torus.
496 */
497float QTorusGeometry::radius() const
498{
499 Q_D(const QTorusGeometry);
500 return d->m_radius;
501}
502
503/*!
504 * \property QTorusGeometry::minorRadius
505 *
506 * Holds the inner radius of the torus.
507 */
508float QTorusGeometry::minorRadius() const
509{
510 Q_D(const QTorusGeometry);
511 return d->m_minorRadius;
512}
513
514/*!
515 * \property QTorusGeometry::positionAttribute
516 *
517 * Holds the geometry position attribute.
518 */
519QAttribute *QTorusGeometry::positionAttribute() const
520{
521 Q_D(const QTorusGeometry);
522 return d->m_positionAttribute;
523}
524
525/*!
526 * \property QTorusGeometry::normalAttribute
527 *
528 * Holds the geometry normal attribute.
529 */
530QAttribute *QTorusGeometry::normalAttribute() const
531{
532 Q_D(const QTorusGeometry);
533 return d->m_normalAttribute;
534}
535
536/*!
537 * \property QTorusGeometry::texCoordAttribute
538 *
539 * Holds the geometry texture coordinate attribute.
540 */
541QAttribute *QTorusGeometry::texCoordAttribute() const
542{
543 Q_D(const QTorusGeometry);
544 return d->m_texCoordAttribute;
545}
546
547/*!
548 * \property QTorusGeometry::indexAttribute
549 *
550 * Holds the geometry index attribute.
551 */
552QAttribute *QTorusGeometry::indexAttribute() const
553{
554 Q_D(const QTorusGeometry);
555 return d->m_indexAttribute;
556}
557
558} // Qt3DExtras
559
560QT_END_NAMESPACE
561

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