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

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