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

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