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#ifndef _USE_MATH_DEFINES
41# define _USE_MATH_DEFINES // For MSVC
42#endif
43
44#include "qcylindergeometry.h"
45#include "qcylindergeometry_p.h"
46
47#include <Qt3DRender/qbuffer.h>
48#include <Qt3DRender/qbufferdatagenerator.h>
49#include <Qt3DRender/qattribute.h>
50#include <QtGui/QVector3D>
51
52#include <qmath.h>
53
54
55QT_BEGIN_NAMESPACE
56
57using namespace Qt3DRender;
58
59namespace Qt3DExtras {
60
61namespace {
62
63int faceCount(int slices, int rings)
64{
65 return (slices * 2) * (rings - 1) // two tris per side, for each pair of adjacent rings
66 + slices * 2; // two caps
67}
68
69int vertexCount(int slices, int rings)
70{
71 return (slices + 1) * rings + 2 * (slices + 1) + 2;
72}
73
74void createSidesVertices(float *&verticesPtr,
75 int rings,
76 int slices,
77 double radius,
78 double length)
79{
80 const float dY = length / static_cast<float>(rings - 1);
81 const float dTheta = (M_PI * 2) / static_cast<float>(slices);
82
83 for (int ring = 0; ring < rings; ++ring) {
84 const float y = -length / 2.0f + static_cast<float>(ring) * dY;
85
86 for (int slice = 0; slice <= slices; ++slice) {
87 const float theta = static_cast<float>(slice) * dTheta;
88 const float ct = qCos(v: theta);
89 const float st = qSin(v: theta);
90
91 *verticesPtr++ = radius * ct;
92 *verticesPtr++ = y;
93 *verticesPtr++ = radius * st;
94
95 *verticesPtr++ = (y + length / 2.0) / length;
96 *verticesPtr++ = theta / (M_PI * 2);
97
98 QVector3D n(ct, 0.0f, st);
99 n.normalize();
100 *verticesPtr++ = n.x();
101 *verticesPtr++ = n.y();
102 *verticesPtr++ = n.z();
103 }
104 }
105}
106
107void createSidesIndices(quint16 *&indicesPtr, int rings, int slices)
108{
109 for (int ring = 0; ring < rings - 1; ++ring) {
110 const int ringIndexStart = ring * (slices + 1);
111 const int nextRingIndexStart = (ring + 1) * (slices + 1);
112
113 for (int slice = 0; slice < slices; ++slice) {
114 const int nextSlice = slice + 1;
115
116 *indicesPtr++ = (ringIndexStart + slice);
117 *indicesPtr++ = (nextRingIndexStart + slice);
118 *indicesPtr++ = (ringIndexStart + nextSlice);
119 *indicesPtr++ = (ringIndexStart + nextSlice);
120 *indicesPtr++ = (nextRingIndexStart + slice);
121 *indicesPtr++ = (nextRingIndexStart + nextSlice);
122 }
123 }
124}
125
126void createDiscVertices(float *&verticesPtr,
127 int slices,
128 double radius,
129 double yPosition)
130{
131 const float dTheta = (M_PI * 2) / static_cast<float>(slices);
132 const double yNormal = (yPosition < 0.0f) ? -1.0f : 1.0f;
133
134 *verticesPtr++ = 0.0f;
135 *verticesPtr++ = yPosition;
136 *verticesPtr++ = 0.0f;
137
138 *verticesPtr++ = 1.0f;
139 *verticesPtr++ = 0.0f;
140
141 *verticesPtr++ = 0.0f;
142 *verticesPtr++ = yNormal;
143 *verticesPtr++ = 0.0f;
144
145 for (int slice = 0; slice <= slices; ++slice) {
146 const float theta = static_cast<float>(slice) * dTheta;
147 const float ct = qCos(v: theta);
148 const float st = qSin(v: theta);
149
150 *verticesPtr++ = radius * ct;
151 *verticesPtr++ = yPosition;
152 *verticesPtr++ = radius * st;
153
154 *verticesPtr++ = 1.0f;
155 *verticesPtr++ = theta / (M_PI * 2);
156
157 *verticesPtr++ = 0.0f;
158 *verticesPtr++ = yNormal;
159 *verticesPtr++ = 0.0f;
160 }
161}
162
163void createDiscIndices(quint16 *&indicesPtr,
164 int discCenterIndex,
165 int slices,
166 double yPosition)
167{
168 const double yNormal = (yPosition < 0.0f) ? -1.0f : 1.0f;
169
170 for (int slice = 0; slice < slices; ++slice) {
171 const int nextSlice = slice + 1;
172
173 *indicesPtr++ = discCenterIndex;
174 *indicesPtr++ = (discCenterIndex + 1 + nextSlice);
175 *indicesPtr++ = (discCenterIndex + 1 + slice);
176
177 if (yNormal < 0.0f)
178 qSwap(value1&: *(indicesPtr -1), value2&: *(indicesPtr - 2));
179 }
180}
181
182} // anonymous
183
184
185class CylinderVertexDataFunctor : public QBufferDataGenerator
186{
187public:
188 CylinderVertexDataFunctor(int rings, int slices, float radius, float length)
189 : m_rings(rings)
190 , m_slices(slices)
191 , m_radius(radius)
192 , m_length(length)
193 {}
194
195 QByteArray operator ()() override
196 {
197 const int verticesCount = vertexCount(slices: m_slices, rings: m_rings);
198 // vec3 pos, vec2 texCoord, vec3 normal
199 const quint32 vertexSize = (3 + 2 + 3) * sizeof(float);
200
201 QByteArray verticesData;
202 verticesData.resize(size: vertexSize * verticesCount);
203 float *verticesPtr = reinterpret_cast<float*>(verticesData.data());
204
205 createSidesVertices(verticesPtr, rings: m_rings, slices: m_slices, radius: m_radius, length: m_length);
206 createDiscVertices(verticesPtr, slices: m_slices, radius: m_radius, yPosition: -m_length * 0.5f);
207 createDiscVertices(verticesPtr, slices: m_slices, radius: m_radius, yPosition: m_length * 0.5f);
208
209 return verticesData;
210 }
211
212 bool operator ==(const QBufferDataGenerator &other) const override
213 {
214 const CylinderVertexDataFunctor *otherFunctor = functor_cast<CylinderVertexDataFunctor>(other: &other);
215 if (otherFunctor != nullptr)
216 return (otherFunctor->m_rings == m_rings &&
217 otherFunctor->m_slices == m_slices &&
218 otherFunctor->m_radius == m_radius &&
219 otherFunctor->m_length == m_length);
220 return false;
221 }
222
223 QT3D_FUNCTOR(CylinderVertexDataFunctor)
224
225private:
226 int m_rings;
227 int m_slices;
228 float m_radius;
229 float m_length;
230};
231
232class CylinderIndexDataFunctor : public QBufferDataGenerator
233{
234public:
235 CylinderIndexDataFunctor(int rings, int slices, float length)
236 : m_rings(rings)
237 , m_slices(slices)
238 , m_length(length)
239 {
240 }
241
242 QByteArray operator ()() override
243 {
244 const int facesCount = faceCount(slices: m_slices, rings: m_rings);
245 const int indicesCount = facesCount * 3;
246 const int indexSize = sizeof(quint16);
247 Q_ASSERT(indicesCount < 65536);
248
249 QByteArray indicesBytes;
250 indicesBytes.resize(size: indicesCount * indexSize);
251 quint16 *indicesPtr = reinterpret_cast<quint16*>(indicesBytes.data());
252
253 createSidesIndices(indicesPtr, rings: m_rings, slices: m_slices);
254 createDiscIndices(indicesPtr, discCenterIndex: m_rings * (m_slices + 1), slices: m_slices, yPosition: -m_length * 0.5);
255 createDiscIndices(indicesPtr, discCenterIndex: m_rings * (m_slices + 1) + m_slices + 2, slices: m_slices, yPosition: m_length * 0.5);
256 Q_ASSERT(indicesPtr == (reinterpret_cast<quint16*>(indicesBytes.data()) + indicesCount));
257
258 return indicesBytes;
259 }
260
261 bool operator ==(const QBufferDataGenerator &other) const override
262 {
263 const CylinderIndexDataFunctor *otherFunctor = functor_cast<CylinderIndexDataFunctor>(other: &other);
264 if (otherFunctor != nullptr)
265 return (otherFunctor->m_rings == m_rings &&
266 otherFunctor->m_slices == m_slices &&
267 otherFunctor->m_length == m_length);
268 return false;
269 }
270
271 QT3D_FUNCTOR(CylinderIndexDataFunctor)
272
273private:
274 int m_rings;
275 int m_slices;
276 float m_length;
277};
278
279
280QCylinderGeometryPrivate::QCylinderGeometryPrivate()
281 : QGeometryPrivate()
282 , m_rings(16)
283 , m_slices(16)
284 , m_radius(1.0f)
285 , m_length(1.0f)
286 , m_positionAttribute(nullptr)
287 , m_normalAttribute(nullptr)
288 , m_texCoordAttribute(nullptr)
289 , m_indexAttribute(nullptr)
290 , m_vertexBuffer(nullptr)
291 , m_indexBuffer(nullptr)
292{
293}
294
295void QCylinderGeometryPrivate::init()
296{
297 Q_Q(QCylinderGeometry);
298 m_positionAttribute = new QAttribute(q);
299 m_normalAttribute = new QAttribute(q);
300 m_texCoordAttribute = new QAttribute(q);
301 m_indexAttribute = new QAttribute(q);
302 m_vertexBuffer = new Qt3DRender::QBuffer(q);
303 m_indexBuffer = new Qt3DRender::QBuffer(q);
304
305 // vec3 pos, vec2 tex, vec3 normal
306 const quint32 elementSize = 3 + 2 + 3;
307 const quint32 stride = elementSize * sizeof(float);
308 const int nVerts = vertexCount(slices: m_slices, rings: m_rings);
309 const int faces = faceCount(slices: m_slices, rings: m_rings);
310
311 m_positionAttribute->setName(QAttribute::defaultPositionAttributeName());
312 m_positionAttribute->setVertexBaseType(QAttribute::Float);
313 m_positionAttribute->setVertexSize(3);
314 m_positionAttribute->setAttributeType(QAttribute::VertexAttribute);
315 m_positionAttribute->setBuffer(m_vertexBuffer);
316 m_positionAttribute->setByteStride(stride);
317 m_positionAttribute->setCount(nVerts);
318
319 m_texCoordAttribute->setName(QAttribute::defaultTextureCoordinateAttributeName());
320 m_texCoordAttribute->setVertexBaseType(QAttribute::Float);
321 m_texCoordAttribute->setVertexSize(2);
322 m_texCoordAttribute->setAttributeType(QAttribute::VertexAttribute);
323 m_texCoordAttribute->setBuffer(m_vertexBuffer);
324 m_texCoordAttribute->setByteStride(stride);
325 m_texCoordAttribute->setByteOffset(3 * sizeof(float));
326 m_texCoordAttribute->setCount(nVerts);
327
328 m_normalAttribute->setName(QAttribute::defaultNormalAttributeName());
329 m_normalAttribute->setVertexBaseType(QAttribute::Float);
330 m_normalAttribute->setVertexSize(3);
331 m_normalAttribute->setAttributeType(QAttribute::VertexAttribute);
332 m_normalAttribute->setBuffer(m_vertexBuffer);
333 m_normalAttribute->setByteStride(stride);
334 m_normalAttribute->setByteOffset(5 * sizeof(float));
335 m_normalAttribute->setCount(nVerts);
336
337 m_indexAttribute->setAttributeType(QAttribute::IndexAttribute);
338 m_indexAttribute->setVertexBaseType(QAttribute::UnsignedShort);
339 m_indexAttribute->setBuffer(m_indexBuffer);
340
341 m_indexAttribute->setCount(faces * 3);
342
343 m_vertexBuffer->setDataGenerator(QSharedPointer<CylinderVertexDataFunctor>::create(arguments&: m_rings, arguments&: m_slices, arguments&: m_radius, arguments&: m_length));
344 m_indexBuffer->setDataGenerator(QSharedPointer<CylinderIndexDataFunctor>::create(arguments&: m_rings, arguments&: m_slices, arguments&: m_length));
345
346 q->addAttribute(attribute: m_positionAttribute);
347 q->addAttribute(attribute: m_texCoordAttribute);
348 q->addAttribute(attribute: m_normalAttribute);
349 q->addAttribute(attribute: m_indexAttribute);
350}
351
352/*!
353 * \qmltype CylinderGeometry
354 * \instantiates Qt3DExtras::QCylinderGeometry
355 * \inqmlmodule Qt3D.Extras
356 * \brief CylinderGeometry allows creation of a cylinder in 3D space.
357 *
358 * The CylinderGeometry type is most commonly used internally by the CylinderMesh type
359 * but can also be used in custom GeometryRenderer types.
360 */
361
362/*!
363 * \qmlproperty int CylinderGeometry::rings
364 *
365 * Holds the number of rings in the cylinder.
366 */
367
368/*!
369 * \qmlproperty int CylinderGeometry::slices
370 *
371 * Holds the number of slices in the cylinder.
372 */
373
374/*!
375 * \qmlproperty real CylinderGeometry::radius
376 *
377 * Holds the radius of the cylinder.
378 */
379
380/*!
381 * \qmlproperty real CylinderGeometry::length
382 *
383 * Holds the length of the cylinder.
384 */
385
386/*!
387 * \qmlproperty Attribute CylinderGeometry::positionAttribute
388 *
389 * Holds the geometry position attribute.
390 */
391
392/*!
393 * \qmlproperty Attribute CylinderGeometry::normalAttribute
394 *
395 * Holds the geometry normal attribute.
396 */
397
398/*!
399 * \qmlproperty Attribute CylinderGeometry::texCoordAttribute
400 *
401 * Holds the geometry texture coordinate attribute.
402 */
403
404/*!
405 * \qmlproperty Attribute CylinderGeometry::indexAttribute
406 *
407 * Holds the geometry index attribute.
408 */
409
410/*!
411 * \class Qt3DExtras::QCylinderGeometry
412 \ingroup qt3d-extras-geometries
413 * \inheaderfile Qt3DExtras/QCylinderGeometry
414 * \inmodule Qt3DExtras
415 * \brief The QCylinderGeometry class allows creation of a cylinder in 3D space.
416 * \since 5.7
417 * \ingroup geometries
418 * \inherits Qt3DRender::QGeometry
419 *
420 * The QCylinderGeometry class is most commonly used internally by the QCylinderMesh
421 * but can also be used in custom Qt3DRender::QGeometryRenderer subclasses.
422 */
423
424/*!
425 * Constructs a new QCylinderMesh with \a parent.
426 */
427QCylinderGeometry::QCylinderGeometry(QNode *parent)
428 : QGeometry(*new QCylinderGeometryPrivate, parent)
429{
430 Q_D(QCylinderGeometry);
431 d->init();
432}
433
434/*!
435 * \internal
436 */
437QCylinderGeometry::QCylinderGeometry(QCylinderGeometryPrivate &dd, QNode *parent)
438 :QGeometry(dd, parent)
439{
440 Q_D(QCylinderGeometry);
441 d->init();
442}
443
444/*!
445 * \internal
446 */
447QCylinderGeometry::~QCylinderGeometry()
448{
449}
450
451/*!
452 * Updates the vertices based on rings, slices, and length properties.
453 */
454void QCylinderGeometry::updateVertices()
455{
456 Q_D(QCylinderGeometry);
457 const int nVerts = vertexCount(slices: d->m_slices, rings: d->m_rings);
458 d->m_positionAttribute->setCount(nVerts);
459 d->m_texCoordAttribute->setCount(nVerts);
460 d->m_normalAttribute->setCount(nVerts);
461
462 d->m_vertexBuffer->setDataGenerator(QSharedPointer<CylinderVertexDataFunctor>::create(arguments&: d->m_rings, arguments&: d->m_slices, arguments&: d->m_radius, arguments&: d->m_length));
463}
464
465/*!
466 * Updates the indices based on rings, slices, and length properties.
467 */
468void QCylinderGeometry::updateIndices()
469{
470 Q_D(QCylinderGeometry);
471 const int faces = faceCount(slices: d->m_slices, rings: d->m_rings);
472 d->m_indexAttribute->setCount(faces * 3);
473 d->m_indexBuffer->setDataGenerator(QSharedPointer<CylinderIndexDataFunctor>::create(arguments&: d->m_rings, arguments&: d->m_slices, arguments&: d->m_length));
474}
475
476void QCylinderGeometry::setRings(int rings)
477{
478 Q_D(QCylinderGeometry);
479 if (rings != d->m_rings) {
480 d->m_rings = rings;
481 updateVertices();
482 updateIndices();
483 emit ringsChanged(rings);
484 }
485}
486
487void QCylinderGeometry::setSlices(int slices)
488{
489 Q_D(QCylinderGeometry);
490 if (slices != d->m_slices) {
491 d->m_slices = slices;
492 updateVertices();
493 updateIndices();
494 emit slicesChanged(slices);
495 }
496}
497
498void QCylinderGeometry::setRadius(float radius)
499{
500 Q_D(QCylinderGeometry);
501 if (radius != d->m_radius) {
502 d->m_radius = radius;
503 updateVertices();
504 emit radiusChanged(radius);
505 }
506}
507
508void QCylinderGeometry::setLength(float length)
509{
510 Q_D(QCylinderGeometry);
511 if (length != d->m_length) {
512 d->m_length = length;
513 updateVertices();
514 updateIndices();
515 emit lengthChanged(length);
516 }
517}
518
519/*!
520 * \property QCylinderGeometry::rings
521 *
522 * Holds the number of rings in the cylinder.
523 */
524int QCylinderGeometry::rings() const
525{
526 Q_D(const QCylinderGeometry);
527 return d->m_rings;
528}
529
530/*!
531 * \property QCylinderGeometry::slices
532 *
533 * Holds the number of slices in the cylinder.
534 */
535int QCylinderGeometry::slices() const
536{
537 Q_D(const QCylinderGeometry);
538 return d->m_slices;
539}
540
541/*!
542 * \property QCylinderGeometry::radius
543 *
544 * Holds the radius of the cylinder.
545 */
546float QCylinderGeometry::radius() const
547{
548 Q_D(const QCylinderGeometry);
549 return d->m_radius;
550}
551
552/*!
553 * \property QCylinderGeometry::length
554 *
555 * Holds the length of the cylinder.
556 */
557float QCylinderGeometry::length() const
558{
559 Q_D(const QCylinderGeometry);
560 return d->m_length;
561}
562
563/*!
564 * \property QCylinderGeometry::positionAttribute
565 *
566 * Holds the geometry position attribute.
567 */
568QAttribute *QCylinderGeometry::positionAttribute() const
569{
570 Q_D(const QCylinderGeometry);
571 return d->m_positionAttribute;
572}
573
574/*!
575 * \property QCylinderGeometry::normalAttribute
576 *
577 * Holds the geometry normal attribute.
578 */
579QAttribute *QCylinderGeometry::normalAttribute() const
580{
581 Q_D(const QCylinderGeometry);
582 return d->m_normalAttribute;
583}
584
585/*!
586 * \property QCylinderGeometry::texCoordAttribute
587 *
588 * Holds the geometry texture coordinate attribute.
589 */
590QAttribute *QCylinderGeometry::texCoordAttribute() const
591{
592 Q_D(const QCylinderGeometry);
593 return d->m_texCoordAttribute;
594}
595
596/*!
597 * \property QCylinderGeometry::indexAttribute
598 *
599 * Holds the geometry index attribute.
600 */
601QAttribute *QCylinderGeometry::indexAttribute() const
602{
603 Q_D(const QCylinderGeometry);
604 return d->m_indexAttribute;
605}
606
607} // namespace Qt3DExtras
608
609QT_END_NAMESPACE
610

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