Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
capsulegeometry.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
6// Based on:
7// https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db
8
10
11#if QT_CONFIG(concurrent)
12#include <QtConcurrentRun>
13#endif
14#include <QQuaternion>
15
16#include <cmath>
17
18QT_BEGIN_NAMESPACE
19
20/*!
21 \qmltype CapsuleGeometry
22 \inqmlmodule QtQuick3D.Helpers
23 \inherits Geometry
24 \since 6.10
25 \brief Provides geometry for a capsule.
26
27 A geometry for generating a capsule model. The capsule is centered
28 at \c{(0, 0, 0)} with the height of the capsule extending in the x
29 direction and the diameter on the yz plane.
30*/
31
32/*! \qmlproperty bool CapsuleGeometry::enableNormals
33 \default true
34
35 Generate mesh face normals.
36*/
37
38/*! \qmlproperty bool CapsuleGeometry::enableUV
39 \default false
40
41 Generate mesh uv coordinates.
42*/
43
44/*! \qmlproperty int CapsuleGeometry::longitudes
45 \default 32
46
47 Number of longitudes, or meridians, distributed by azimuth.
48*/
49
50/*! \qmlproperty int CapsuleGeometry::latitudes
51 \default 16
52
53 Number of latitudes, distributed by inclination. Will always be snapped to an even number.
54*/
55
56/*! \qmlproperty int CapsuleGeometry::rings
57 \default 1
58
59 Number of sections in cylinder between hemispheres.
60*/
61
62/*! \qmlproperty real CapsuleGeometry::height
63 \default 100
64
65 Height of the middle cylinder on the x axis, excluding the hemispheres.
66*/
67
68/*! \qmlproperty real CapsuleGeometry::diameter
69 \default 100
70
71 Diameter on the yz plane.
72*/
73
74/*!
75 \qmlproperty UVProfile CapsuleGeometry::uvProfile
76 \default CapsuleGeometry.Fixed
77
78 Manner in which UV coordinates are distributed along the length of the capsule.
79
80 \value CapsuleGeometry.Fixed The upper third of the UV texture is the North
81 hemisphere, the middle third is the cylinder and the last third is the
82 South hemisphere.
83 \value CapsuleGeometry.Aspect UVs match the height to radius ratio.
84 \value CapsuleGeometry.Uniform Uniform proportion for all UV cells,
85 according to the ratio of latitudes to rings.
86*/
87
88/*!
89 \qmlproperty bool CapsuleGeometry::asynchronous
90 \default true
91
92 This property holds whether the geometry generation should be asynchronous.
93*/
94
95/*!
96 \qmlproperty bool CapsuleGeometry::status
97 \readonly
98
99 This property holds the status of the geometry generation when asynchronous is true.
100
101 \value CapsuleGeometry.Null The geometry generation has not started
102 \value CapsuleGeometry.Ready The geometry generation is complete.
103 \value CapsuleGeometry.Loading The geometry generation is in progress.
104 \value CapsuleGeometry.Error The geometry generation failed.
105*/
106
107CapsuleGeometry::CapsuleGeometry(QQuick3DObject *parent) : QQuick3DGeometry(parent)
108{
109#if QT_CONFIG(concurrent)
110 connect(&m_geometryDataWatcher, &QFutureWatcher<GeometryData>::finished, this, &CapsuleGeometry::requestFinished);
111#endif
112 scheduleGeometryUpdate();
113}
114
115CapsuleGeometry::~CapsuleGeometry() = default;
116
117void CapsuleGeometry::setEnableNormals(bool enable)
118{
119 if (m_enableNormals == enable)
120 return;
121
122 m_enableNormals = enable;
123 emit enableNormalsChanged();
124 scheduleGeometryUpdate();
125}
126
127void CapsuleGeometry::setEnableUV(bool enable)
128{
129 if (m_enableUV == enable)
130 return;
131
132 m_enableUV = enable;
133 emit enableUVChanged();
134 scheduleGeometryUpdate();
135}
136
137void CapsuleGeometry::setLongitudes(int longitudes)
138{
139 if (m_longitudes == longitudes)
140 return;
141
142 m_longitudes = longitudes;
143 emit longitudesChanged();
144 scheduleGeometryUpdate();
145}
146
147void CapsuleGeometry::setLatitudes(int latitudes)
148{
149 if (m_latitudes == latitudes)
150 return;
151
152 m_latitudes = latitudes;
153 emit latitudesChanged();
154 scheduleGeometryUpdate();
155}
156
157void CapsuleGeometry::setRings(int rings)
158{
159 if (m_rings == rings)
160 return;
161
162 m_rings = rings;
163 emit ringsChanged();
164 scheduleGeometryUpdate();
165}
166
167void CapsuleGeometry::setHeight(float height)
168{
169 if (m_height == height)
170 return;
171
172 m_height = height;
173 emit heightChanged();
174 scheduleGeometryUpdate();
175}
176
177void CapsuleGeometry::setDiameter(float diameter)
178{
179 if (m_diameter == diameter)
180 return;
181
182 m_diameter = diameter;
183 emit diameterChanged();
184 scheduleGeometryUpdate();
185}
186
187CapsuleGeometry::UVProfile CapsuleGeometry::uvProfile() const
188{
189 return m_uvProfile;
190}
191
192void CapsuleGeometry::setUVProfile(UVProfile newUVProfile)
193{
194 if (m_uvProfile == newUVProfile)
195 return;
196 m_uvProfile = newUVProfile;
197 emit uvProfileChanged();
198 scheduleGeometryUpdate();
199}
200
201bool CapsuleGeometry::asynchronous() const
202{
203 return m_asynchronous;
204}
205
206void CapsuleGeometry::setAsynchronous(bool newAsynchronous)
207{
208 if (m_asynchronous == newAsynchronous)
209 return;
210 m_asynchronous = newAsynchronous;
211 emit asynchronousChanged();
212}
213
214CapsuleGeometry::Status CapsuleGeometry::status() const
215{
216 return m_status;
217}
218
219void CapsuleGeometry::doUpdateGeometry()
220{
221 // reset the flag since we are processing the update
222 m_geometryUpdateRequested = false;
223
224#if QT_CONFIG(concurrent)
225 if (m_geometryDataFuture.isRunning()) {
226 m_pendingAsyncUpdate = true;
227 return;
228 }
229#endif
230
231#if QT_CONFIG(concurrent)
232 // without threads, QtConcurrent::run() would never run the task
233 if (m_asynchronous && QT_CONFIG(thread)) {
234 m_geometryDataFuture = QtConcurrent::run(generateCapsuleGeometryAsync,
235 m_enableNormals,
236 m_enableUV,
237 m_longitudes,
238 m_latitudes,
239 m_rings,
240 m_height,
241 m_diameter,
242 m_uvProfile);
243 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
244 m_status = Status::Loading;
245 Q_EMIT statusChanged();
246 } else {
247#else
248 {
249#endif // QT_CONFIG(concurrent)
250 updateGeometry(generateCapsuleGeometry(m_enableNormals, m_enableUV, m_longitudes, m_latitudes, m_rings, m_height, m_diameter, m_uvProfile));
251 }
252}
253
254void CapsuleGeometry::requestFinished()
255{
256#if QT_CONFIG(concurrent)
257 const auto output = m_geometryDataFuture.takeResult();
258 updateGeometry(output);
259#endif
260}
261
262void CapsuleGeometry::scheduleGeometryUpdate()
263{
264 if (!m_geometryUpdateRequested) {
265 QMetaObject::invokeMethod(this, "doUpdateGeometry", Qt::QueuedConnection);
266 m_geometryUpdateRequested = true;
267 }
268}
269
270void CapsuleGeometry::updateGeometry(const GeometryData &geometryData)
271{
272 clear();
273 setStride(geometryData.stride);
274 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
275 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::F32Type);
276 if (geometryData.enableNormals) {
277 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
278 geometryData.strideNormal,
279 QQuick3DGeometry::Attribute::ComponentType::F32Type);
280 }
281 if (geometryData.enableUV) {
282 addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic, geometryData.strideUV, QQuick3DGeometry::Attribute::ComponentType::F32Type);
283 }
284 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::U32Type);
285
286 setBounds(geometryData.boundsMin, geometryData.boundsMax);
287 setVertexData(geometryData.vertexData);
288 setIndexData(geometryData.indexData);
289
290 // If the geometry update was requested while the geometry was being generated asynchronously,
291 // we need to schedule another geometry update now that the geometry is ready.
292 if (m_pendingAsyncUpdate) {
293 m_pendingAsyncUpdate = false;
294 scheduleGeometryUpdate();
295 } else {
296 m_status = Status::Ready;
297 Q_EMIT statusChanged();
298 }
299 update();
300 emit geometryChanged();
301}
302
303CapsuleGeometry::GeometryData CapsuleGeometry::generateCapsuleGeometry(bool enableNormals,
304 bool enableUV,
305 int longitudes,
306 int latitudes,
307 int rings,
308 float height,
309 float diameter,
310 UVProfile uvProfile)
311{
312 longitudes = qMax(3, longitudes);
313 latitudes = qMax(2, latitudes + (latitudes % 2)); // make even
314 rings = qMax(1, rings);
315 height = qMax(0.00001f, height);
316 diameter = qMax(0.00001f, diameter);
317
318 const UVProfile profile = uvProfile;
319 const float radius = diameter / 2;
320 const float depth = height;
321
322 bool calcMiddle = rings > 0;
323 int halfLats = latitudes / 2;
324 int halfLatsn1 = halfLats - 1;
325 int halfLatsn2 = halfLats - 2;
326 int ringsp1 = rings + 1;
327 int lonsp1 = longitudes + 1;
328 float halfDepth = depth * 0.5f;
329 float summit = halfDepth + radius;
330
331 // Vertex index offsets.
332 int vertOffsetNorthHemi = longitudes;
333 int vertOffsetNorthEquator = vertOffsetNorthHemi + lonsp1 * halfLatsn1;
334 int vertOffsetCylinder = vertOffsetNorthEquator + lonsp1;
335 int vertOffsetSouthEquator = calcMiddle ?
336 vertOffsetCylinder + lonsp1 * rings :
337 vertOffsetCylinder;
338 int vertOffsetSouthHemi = vertOffsetSouthEquator + lonsp1;
339 int vertOffsetSouthPolar = vertOffsetSouthHemi + lonsp1 * halfLatsn2;
340 int vertOffsetSouthCap = vertOffsetSouthPolar + lonsp1;
341
342 // Initialize arrays.
343 int vertLen = vertOffsetSouthCap + longitudes;
344 QList<QVector3D> vs = QList<QVector3D>(vertLen);
345 QList<QVector2D> vts = QList<QVector2D>(vertLen);
346 QList<QVector3D> vns = QList<QVector3D>(vertLen);
347
348 float toTheta = 2.0f * M_PI / longitudes;
349 float toPhi = M_PI / latitudes;
350 float toTexHorizontal = 1.0f / longitudes;
351 float toTexVertical = 1.0f / halfLats;
352
353 // Calculate positions for texture coordinates vertical.
354 float vtAspectRatio = 1.0f;
355 switch (profile)
356 {
357 case UVProfile::Aspect:
358 vtAspectRatio = radius / (depth + radius + radius);
359 break;
360
361 case UVProfile::Uniform:
362 vtAspectRatio = (float) halfLats / (ringsp1 + latitudes);
363 break;
364
365 case UVProfile::Fixed:
366 default:
367 vtAspectRatio = 1.0f / 3.0f;
368 break;
369 }
370
371 float vtAspectNorth = 1.0f - vtAspectRatio;
372 float vtAspectSouth = vtAspectRatio;
373
374 QList<QVector2D> thetaCartesian = QList<QVector2D>(longitudes);
375 QList<QVector2D> rhoThetaCartesian = QList<QVector2D>(longitudes);
376 QList<float> sTextureCache = QList<float>(lonsp1);
377
378 // Polar vertices.
379 for (int j = 0; j < longitudes; ++j)
380 {
381 float jf = j;
382 float sTexturePolar = 1.0f - ((jf + 0.5f) * toTexHorizontal);
383 float theta = jf * toTheta;
384
385 float cosTheta = std::cos(theta);
386 float sinTheta = std::sin(theta);
387
388 thetaCartesian[j] = QVector2D(cosTheta, sinTheta);
389 rhoThetaCartesian[j] = QVector2D(
390 radius * cosTheta,
391 radius * sinTheta);
392
393 // North.
394 vs[j] = QVector3D(0.0f, summit, 0.0f);
395 vts[j] = QVector2D(sTexturePolar, 1.0f);
396 vns[j] = QVector3D(0.0f, 1.0f, 0.0f);
397
398 // South.
399 int idx = vertOffsetSouthCap + j;
400 vs[idx] = QVector3D(0.0f, -summit, 0.0f);
401 vts[idx] = QVector2D(sTexturePolar, 0.0f);
402 vns[idx] = QVector3D(0.0f, -1.0f, 0.0f);
403 }
404
405 // Equatorial vertices.
406 for (int j = 0; j < lonsp1; ++j)
407 {
408 float sTexture = 1.0f - j * toTexHorizontal;
409 sTextureCache[j] = sTexture;
410
411 // Wrap to first element upon reaching last.
412 int jMod = j % longitudes;
413 QVector2D tc = thetaCartesian[jMod];
414 QVector2D rtc = rhoThetaCartesian[jMod];
415
416 // North equator.
417 int idxn = vertOffsetNorthEquator + j;
418 vs[idxn] = QVector3D(rtc.x(), halfDepth, -rtc.y());
419 vts[idxn] = QVector2D(sTexture, vtAspectNorth);
420 vns[idxn] = QVector3D(tc.x(), 0.0f, -tc.y());
421
422 // South equator.
423 int idxs = vertOffsetSouthEquator + j;
424 vs[idxs] = QVector3D(rtc.x(), -halfDepth, -rtc.y());
425 vts[idxs] = QVector2D(sTexture, vtAspectSouth);
426 vns[idxs] = QVector3D(tc.x(), 0.0f, -tc.y());
427 }
428
429 // Hemisphere vertices.
430 for (int i = 0; i < halfLatsn1; ++i)
431 {
432 float ip1f = i + 1.0f;
433 float phi = ip1f * toPhi;
434
435 // For coordinates.
436 float cosPhiSouth = std::cos(phi);
437 float sinPhiSouth = std::sin(phi);
438
439 // Symmetrical hemispheres mean cosine and sine only needs
440 // to be calculated once.
441 float cosPhiNorth = sinPhiSouth;
442 float sinPhiNorth = -cosPhiSouth;
443
444 float rhoCosPhiNorth = radius * cosPhiNorth;
445 float rhoSinPhiNorth = radius * sinPhiNorth;
446 float zOffsetNorth = halfDepth - rhoSinPhiNorth;
447
448 float rhoCosPhiSouth = radius * cosPhiSouth;
449 float rhoSinPhiSouth = radius * sinPhiSouth;
450 float zOffsetSouth = -halfDepth - rhoSinPhiSouth;
451
452 // For texture coordinates.
453 float tTexFac = ip1f * toTexVertical;
454 float cmplTexFac = 1.0f - tTexFac;
455 float tTexNorth = cmplTexFac + vtAspectNorth * tTexFac;
456 float tTexSouth = cmplTexFac * vtAspectSouth;
457
458 int iLonsp1 = i * lonsp1;
459 int vertCurrLatNorth = vertOffsetNorthHemi + iLonsp1;
460 int vertCurrLatSouth = vertOffsetSouthHemi + iLonsp1;
461
462 for (int j = 0; j < lonsp1; ++j)
463 {
464 int jMod = j % longitudes;
465
466 float sTexture = sTextureCache[j];
467 QVector2D tc = thetaCartesian[jMod];
468
469 // North hemisphere.
470 int idxn = vertCurrLatNorth + j;
471 vs[idxn] = QVector3D(
472 rhoCosPhiNorth * tc.x(),
473 zOffsetNorth,
474 -rhoCosPhiNorth * tc.y());
475 vts[idxn] = QVector2D(sTexture, tTexNorth);
476 vns[idxn] = QVector3D(
477 cosPhiNorth * tc.x(),
478 -sinPhiNorth,
479 -cosPhiNorth * tc.y());
480
481 // South hemisphere.
482 int idxs = vertCurrLatSouth + j;
483 vs[idxs] = QVector3D(
484 rhoCosPhiSouth * tc.x(),
485 zOffsetSouth,
486 -rhoCosPhiSouth * tc.y());
487 vts[idxs] = QVector2D(sTexture, tTexSouth);
488 vns[idxs] = QVector3D(
489 cosPhiSouth * tc.x(),
490 -sinPhiSouth,
491 -cosPhiSouth * tc.y());
492 }
493 }
494
495 // Cylinder vertices.
496 if (calcMiddle)
497 {
498 // Exclude both origin and destination edges
499 // (North and South equators) from the interpolation.
500 float toFac = 1.0f / ringsp1;
501 int idxCylLat = vertOffsetCylinder;
502
503 for (int h = 1; h < ringsp1; ++h)
504 {
505 float fac = h * toFac;
506 float cmplFac = 1.0f - fac;
507 float tTexture = cmplFac * vtAspectNorth + fac * vtAspectSouth;
508 float z = halfDepth - depth * fac;
509
510 for (int j = 0; j < lonsp1; ++j)
511 {
512 int jMod = j % longitudes;
513 QVector2D tc = thetaCartesian[jMod];
514 QVector2D rtc = rhoThetaCartesian[jMod];
515 float sTexture = sTextureCache[j];
516
517 vs[idxCylLat] = QVector3D(rtc.x(), z, -rtc.y());
518 vts[idxCylLat] = QVector2D(sTexture, tTexture);
519 vns[idxCylLat] = QVector3D(tc.x(), 0.0f, -tc.y());
520
521 ++idxCylLat;
522 }
523 }
524 }
525
526 // Triangle indices.
527 // Stride is 3 for polar triangles;
528 // stride is 6 for two triangles forming a quad.
529 int lons3 = longitudes * 3;
530 int lons6 = longitudes * 6;
531 int hemiLons = halfLatsn1 * lons6;
532
533 int triOffsetNorthHemi = lons3;
534 int triOffsetCylinder = triOffsetNorthHemi + hemiLons;
535 int triOffsetSouthHemi = triOffsetCylinder + ringsp1 * lons6;
536 int triOffsetSouthCap = triOffsetSouthHemi + hemiLons;
537
538 int fsLen = triOffsetSouthCap + lons3;
539 QList<int> tris = QList<int>(fsLen);
540
541 // Polar caps.
542 for (int i = 0, k = 0, m = triOffsetSouthCap; i < longitudes; ++i, k += 3, m += 3)
543 {
544 // North.
545 tris[k] = i;
546 tris[k + 1] = vertOffsetNorthHemi + i;
547 tris[k + 2] = vertOffsetNorthHemi + i + 1;
548
549 // South.
550 tris[m] = vertOffsetSouthCap + i;
551 tris[m + 1] = vertOffsetSouthPolar + i + 1;
552 tris[m + 2] = vertOffsetSouthPolar + i;
553 }
554
555 // Hemispheres.
556 for (int i = 0, k = triOffsetNorthHemi, m = triOffsetSouthHemi; i < halfLatsn1; ++i)
557 {
558 int iLonsp1 = i * lonsp1;
559
560 int vertCurrLatNorth = vertOffsetNorthHemi + iLonsp1;
561 int vertNextLatNorth = vertCurrLatNorth + lonsp1;
562
563 int vertCurrLatSouth = vertOffsetSouthEquator + iLonsp1;
564 int vertNextLatSouth = vertCurrLatSouth + lonsp1;
565
566 for (int j = 0; j < longitudes; ++j, k += 6, m += 6)
567 {
568 // North.
569 int north00 = vertCurrLatNorth + j;
570 int north01 = vertNextLatNorth + j;
571 int north11 = vertNextLatNorth + j + 1;
572 int north10 = vertCurrLatNorth + j + 1;
573
574 tris[k] = north00;
575 tris[k + 1] = north11;
576 tris[k + 2] = north10;
577
578 tris[k + 3] = north00;
579 tris[k + 4] = north01;
580 tris[k + 5] = north11;
581
582 // South.
583 int south00 = vertCurrLatSouth + j;
584 int south01 = vertNextLatSouth + j;
585 int south11 = vertNextLatSouth + j + 1;
586 int south10 = vertCurrLatSouth + j + 1;
587
588 tris[m] = south00;
589 tris[m + 1] = south11;
590 tris[m + 2] = south10;
591
592 tris[m + 3] = south00;
593 tris[m + 4] = south01;
594 tris[m + 5] = south11;
595 }
596 }
597
598 // Cylinder.
599 for (int i = 0, k = triOffsetCylinder; i < ringsp1; ++i)
600 {
601 int vertCurrLat = vertOffsetNorthEquator + i * lonsp1;
602 int vertNextLat = vertCurrLat + lonsp1;
603
604 for (int j = 0; j < longitudes; ++j, k += 6)
605 {
606 int cy00 = vertCurrLat + j;
607 int cy01 = vertNextLat + j;
608 int cy11 = vertNextLat + j + 1;
609 int cy10 = vertCurrLat + j + 1;
610
611 tris[k] = cy00;
612 tris[k + 1] = cy11;
613 tris[k + 2] = cy10;
614
615 tris[k + 3] = cy00;
616 tris[k + 4] = cy01;
617 tris[k + 5] = cy11;
618 }
619 }
620
621 auto vertices = vs;
622 auto vertexTextures = vts;
623 auto vertexNormals = vns;
624
625 uint32_t stride = 3 * sizeof(float);
626 uint32_t strideNormal = 0;
627 uint32_t strideUV = 0;
628
629 if (enableNormals) {
630 strideNormal = stride;
631 stride += 3 * sizeof(float);
632 }
633 if (enableUV) {
634 strideUV = stride;
635 stride += 2 * sizeof(float);
636 }
637
638 QByteArray vertexData(vertices.length() * stride, Qt::Initialization::Uninitialized);
639 QByteArray indexData(tris.length() * sizeof(int), Qt::Initialization::Uninitialized);
640
641 const auto getVertexPtr = [&](const int vertexIdx) {
642 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx);
643 };
644 const auto getNormalPtr = [&](const int vertexIdx) {
645 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx + strideNormal);
646 };
647 const auto getTexturePtr = [&](const int vertexIdx) {
648 return reinterpret_cast<QVector2D *>(vertexData.data() + stride * vertexIdx + strideUV);
649 };
650
651 const QQuaternion rotateZ = QQuaternion::fromEulerAngles(0, 0, 90);
652 uint32_t *indexPtr = reinterpret_cast<uint32_t *>(indexData.data());
653
654 for (qsizetype i = 0; i < vertices.length(); i++) {
655 *getVertexPtr(i) = rotateZ * vertices[i];
656 }
657
658 for (qsizetype i = 0; i < tris.length() / 3; i++) {
659 std::array<int, 3> triIndices = { tris[i * 3], tris[i * 3 + 1], tris[i * 3 + 2] };
660 *indexPtr = triIndices[0];
661 indexPtr++;
662 *indexPtr = triIndices[1];
663 indexPtr++;
664 *indexPtr = triIndices[2];
665 indexPtr++;
666
667 if (enableNormals) {
668 *getNormalPtr(triIndices[0]) = rotateZ * vertexNormals[triIndices[0]];
669 *getNormalPtr(triIndices[1]) = rotateZ * vertexNormals[triIndices[1]];
670 *getNormalPtr(triIndices[2]) = rotateZ * vertexNormals[triIndices[2]];
671 }
672
673 if (enableUV) {
674 *getTexturePtr(triIndices[0]) = vertexTextures[triIndices[0]];
675 *getTexturePtr(triIndices[1]) = vertexTextures[triIndices[1]];
676 *getTexturePtr(triIndices[2]) = vertexTextures[triIndices[2]];
677 }
678 }
679
680 GeometryData geometryData;
681 geometryData.indexData = indexData;
682 geometryData.vertexData = vertexData;
683 geometryData.boundsMin = QVector3D(-radius - halfDepth, -radius, -radius);
684 geometryData.boundsMax = QVector3D(radius + halfDepth, radius, radius);
685 geometryData.stride = stride;
686 geometryData.strideNormal = strideNormal;
687 geometryData.strideUV = strideUV;
688 geometryData.enableNormals = enableNormals;
689 geometryData.enableUV = enableUV;
690 return geometryData;
691}
692
693#if QT_CONFIG(concurrent)
694void CapsuleGeometry::generateCapsuleGeometryAsync(QPromise<CapsuleGeometry::GeometryData> &promise,
695 bool enableNormals,
696 bool enableUV,
697 int longitudes,
698 int latitudes,
699 int rings,
700 float height,
701 float diameter,
702 CapsuleGeometry::UVProfile uvProfile)
703{
704 auto output = generateCapsuleGeometry(enableNormals, enableUV, longitudes, latitudes, rings, height, diameter, uvProfile);
705 promise.addResult(output);
706}
707#endif
708
709QT_END_NAMESPACE
#define M_PI
Definition qmath.h:201