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 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | |
21 | namespace Qt3DExtras { |
22 | |
23 | using namespace Qt3DCore; |
24 | |
25 | namespace { |
26 | |
27 | int (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 | |
33 | int (int slices, int rings) |
34 | { |
35 | return (slices + 1) * rings + 2 * (slices + 1) + 2; |
36 | } |
37 | |
38 | void (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 | |
71 | void (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 | |
90 | void (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 | |
127 | void (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 | |
148 | 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 | |
163 | void QCylinderGeometryPrivate::() |
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 | |
220 | QByteArray QCylinderGeometryPrivate::() 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 | |
237 | QByteArray QCylinderGeometryPrivate::() 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 | */ |
331 | QCylinderGeometry::(QNode *parent) |
332 | : QGeometry(*new QCylinderGeometryPrivate, parent) |
333 | { |
334 | Q_D(QCylinderGeometry); |
335 | d->init(); |
336 | } |
337 | |
338 | /*! |
339 | * \internal |
340 | */ |
341 | QCylinderGeometry::(QCylinderGeometryPrivate &dd, QNode *parent) |
342 | :QGeometry(dd, parent) |
343 | { |
344 | Q_D(QCylinderGeometry); |
345 | d->init(); |
346 | } |
347 | |
348 | /*! |
349 | * \internal |
350 | */ |
351 | QCylinderGeometry::() |
352 | { |
353 | } |
354 | |
355 | /*! |
356 | * Updates the vertices based on rings, slices, and length properties. |
357 | */ |
358 | void QCylinderGeometry::() |
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 | */ |
372 | void QCylinderGeometry::() |
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 | |
380 | void QCylinderGeometry::(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 | |
391 | void QCylinderGeometry::(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 | |
402 | void QCylinderGeometry::(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 | |
412 | void QCylinderGeometry::(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 | */ |
428 | int QCylinderGeometry::() 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 | */ |
439 | int QCylinderGeometry::() 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 | */ |
450 | float QCylinderGeometry::() 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 | */ |
461 | float QCylinderGeometry::() 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 | */ |
472 | QAttribute *QCylinderGeometry::() 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 | */ |
483 | QAttribute *QCylinderGeometry::() 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 | */ |
494 | QAttribute *QCylinderGeometry::() 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 | */ |
505 | QAttribute *QCylinderGeometry::() const |
506 | { |
507 | Q_D(const QCylinderGeometry); |
508 | return d->m_indexAttribute; |
509 | } |
510 | |
511 | } // namespace Qt3DExtras |
512 | |
513 | QT_END_NAMESPACE |
514 | |
515 | #include "moc_qcylindergeometry.cpp" |
516 | |