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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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