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 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | using namespace Qt3DRender; |
58 | |
59 | namespace Qt3DExtras { |
60 | |
61 | namespace { |
62 | |
63 | int (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 | |
69 | int (int slices, int rings) |
70 | { |
71 | return (slices + 1) * rings + 2 * (slices + 1) + 2; |
72 | } |
73 | |
74 | void (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 | |
107 | void (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 | |
126 | void (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 | |
163 | void (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 | |
185 | class : public QBufferDataGenerator |
186 | { |
187 | public: |
188 | (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 () 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 (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 | |
225 | private: |
226 | int ; |
227 | int ; |
228 | float ; |
229 | float ; |
230 | }; |
231 | |
232 | class : public QBufferDataGenerator |
233 | { |
234 | public: |
235 | (int rings, int slices, float length) |
236 | : m_rings(rings) |
237 | , m_slices(slices) |
238 | , m_length(length) |
239 | { |
240 | } |
241 | |
242 | QByteArray () 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 (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 | |
273 | private: |
274 | int ; |
275 | int ; |
276 | float ; |
277 | }; |
278 | |
279 | |
280 | 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 | |
295 | void QCylinderGeometryPrivate::() |
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 | */ |
427 | QCylinderGeometry::(QNode *parent) |
428 | : QGeometry(*new QCylinderGeometryPrivate, parent) |
429 | { |
430 | Q_D(QCylinderGeometry); |
431 | d->init(); |
432 | } |
433 | |
434 | /*! |
435 | * \internal |
436 | */ |
437 | QCylinderGeometry::(QCylinderGeometryPrivate &dd, QNode *parent) |
438 | :QGeometry(dd, parent) |
439 | { |
440 | Q_D(QCylinderGeometry); |
441 | d->init(); |
442 | } |
443 | |
444 | /*! |
445 | * \internal |
446 | */ |
447 | QCylinderGeometry::() |
448 | { |
449 | } |
450 | |
451 | /*! |
452 | * Updates the vertices based on rings, slices, and length properties. |
453 | */ |
454 | void QCylinderGeometry::() |
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 | */ |
468 | void QCylinderGeometry::() |
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 | |
476 | void QCylinderGeometry::(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 | |
487 | void QCylinderGeometry::(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 | |
498 | void QCylinderGeometry::(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 | |
508 | void QCylinderGeometry::(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 | */ |
524 | int QCylinderGeometry::() 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 | */ |
535 | int QCylinderGeometry::() 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 | */ |
546 | float QCylinderGeometry::() 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 | */ |
557 | float QCylinderGeometry::() 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 | */ |
568 | QAttribute *QCylinderGeometry::() 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 | */ |
579 | QAttribute *QCylinderGeometry::() 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 | */ |
590 | QAttribute *QCylinderGeometry::() 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 | */ |
601 | QAttribute *QCylinderGeometry::() const |
602 | { |
603 | Q_D(const QCylinderGeometry); |
604 | return d->m_indexAttribute; |
605 | } |
606 | |
607 | } // namespace Qt3DExtras |
608 | |
609 | QT_END_NAMESPACE |
610 | |