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

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