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

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