11#if QT_CONFIG(concurrent)
12#include <QtConcurrentRun>
21
22
23
24
25
26
27
28
29
30
33
34
35
36
39
40
41
42
45
46
47
48
51
52
53
54
57
58
59
60
63
64
65
66
69
70
71
72
75
76
77
78
79
80
81
82
83
84
85
86
89
90
91
92
93
96
97
98
99
100
101
102
103
104
105
107CapsuleGeometry::CapsuleGeometry(QQuick3DObject *parent) : QQuick3DGeometry(parent)
109#if QT_CONFIG(concurrent)
110 connect(&m_geometryDataWatcher, &QFutureWatcher<GeometryData>::finished,
this, &CapsuleGeometry::requestFinished);
112 scheduleGeometryUpdate();
115CapsuleGeometry::~CapsuleGeometry() =
default;
117void CapsuleGeometry::setEnableNormals(
bool enable)
119 if (m_enableNormals == enable)
122 m_enableNormals = enable;
123 emit enableNormalsChanged();
124 scheduleGeometryUpdate();
127void CapsuleGeometry::setEnableUV(
bool enable)
129 if (m_enableUV == enable)
133 emit enableUVChanged();
134 scheduleGeometryUpdate();
137void CapsuleGeometry::setLongitudes(
int longitudes)
139 if (m_longitudes == longitudes)
142 m_longitudes = longitudes;
143 emit longitudesChanged();
144 scheduleGeometryUpdate();
147void CapsuleGeometry::setLatitudes(
int latitudes)
149 if (m_latitudes == latitudes)
152 m_latitudes = latitudes;
153 emit latitudesChanged();
154 scheduleGeometryUpdate();
157void CapsuleGeometry::setRings(
int rings)
159 if (m_rings == rings)
164 scheduleGeometryUpdate();
167void CapsuleGeometry::setHeight(
float height)
169 if (m_height == height)
173 emit heightChanged();
174 scheduleGeometryUpdate();
177void CapsuleGeometry::setDiameter(
float diameter)
179 if (m_diameter == diameter)
182 m_diameter = diameter;
183 emit diameterChanged();
184 scheduleGeometryUpdate();
187CapsuleGeometry::UVProfile CapsuleGeometry::uvProfile()
const
192void CapsuleGeometry::setUVProfile(UVProfile newUVProfile)
194 if (m_uvProfile == newUVProfile)
196 m_uvProfile = newUVProfile;
197 emit uvProfileChanged();
198 scheduleGeometryUpdate();
201bool CapsuleGeometry::asynchronous()
const
203 return m_asynchronous;
206void CapsuleGeometry::setAsynchronous(
bool newAsynchronous)
208 if (m_asynchronous == newAsynchronous)
210 m_asynchronous = newAsynchronous;
211 emit asynchronousChanged();
214CapsuleGeometry::Status CapsuleGeometry::status()
const
219void CapsuleGeometry::doUpdateGeometry()
222 m_geometryUpdateRequested =
false;
224#if QT_CONFIG(concurrent)
225 if (m_geometryDataFuture.isRunning()) {
226 m_pendingAsyncUpdate =
true;
231#if QT_CONFIG(concurrent)
233 if (m_asynchronous && QT_CONFIG(thread)) {
234 m_geometryDataFuture = QtConcurrent::run(generateCapsuleGeometryAsync,
243 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
244 m_status = Status::Loading;
245 Q_EMIT statusChanged();
250 updateGeometry(generateCapsuleGeometry(m_enableNormals, m_enableUV, m_longitudes, m_latitudes, m_rings, m_height, m_diameter, m_uvProfile));
254void CapsuleGeometry::requestFinished()
256#if QT_CONFIG(concurrent)
257 const auto output = m_geometryDataFuture.takeResult();
258 updateGeometry(output);
262void CapsuleGeometry::scheduleGeometryUpdate()
264 if (!m_geometryUpdateRequested) {
265 QMetaObject::invokeMethod(
this,
"doUpdateGeometry", Qt::QueuedConnection);
266 m_geometryUpdateRequested =
true;
270void CapsuleGeometry::updateGeometry(
const GeometryData &geometryData)
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);
281 if (geometryData.enableUV) {
282 addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic, geometryData.strideUV, QQuick3DGeometry::Attribute::ComponentType::F32Type);
284 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::U32Type);
286 setBounds(geometryData.boundsMin, geometryData.boundsMax);
287 setVertexData(geometryData.vertexData);
288 setIndexData(geometryData.indexData);
292 if (m_pendingAsyncUpdate) {
293 m_pendingAsyncUpdate =
false;
294 scheduleGeometryUpdate();
296 m_status = Status::Ready;
297 Q_EMIT statusChanged();
300 emit geometryChanged();
303CapsuleGeometry::GeometryData CapsuleGeometry::generateCapsuleGeometry(
bool enableNormals,
312 longitudes = qMax(3, longitudes);
313 latitudes = qMax(2, latitudes + (latitudes % 2));
314 rings = qMax(1, rings);
315 height = qMax(0.00001f, height);
316 diameter = qMax(0.00001f, diameter);
318 const UVProfile profile = uvProfile;
319 const float radius = diameter / 2;
320 const float depth = height;
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;
332 int vertOffsetNorthHemi = longitudes;
333 int vertOffsetNorthEquator = vertOffsetNorthHemi + lonsp1 * halfLatsn1;
334 int vertOffsetCylinder = vertOffsetNorthEquator + lonsp1;
335 int vertOffsetSouthEquator = calcMiddle ?
336 vertOffsetCylinder + lonsp1 * rings :
338 int vertOffsetSouthHemi = vertOffsetSouthEquator + lonsp1;
339 int vertOffsetSouthPolar = vertOffsetSouthHemi + lonsp1 * halfLatsn2;
340 int vertOffsetSouthCap = vertOffsetSouthPolar + lonsp1;
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);
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;
354 float vtAspectRatio = 1.0f;
357 case UVProfile::Aspect:
358 vtAspectRatio = radius / (depth + radius + radius);
361 case UVProfile::Uniform:
362 vtAspectRatio = (
float) halfLats / (ringsp1 + latitudes);
365 case UVProfile::Fixed:
367 vtAspectRatio = 1.0f / 3.0f;
371 float vtAspectNorth = 1.0f - vtAspectRatio;
372 float vtAspectSouth = vtAspectRatio;
374 QList<QVector2D> thetaCartesian = QList<QVector2D>(longitudes);
375 QList<QVector2D> rhoThetaCartesian = QList<QVector2D>(longitudes);
376 QList<
float> sTextureCache = QList<
float>(lonsp1);
379 for (
int j = 0; j < longitudes; ++j)
382 float sTexturePolar = 1.0f - ((jf + 0.5f) * toTexHorizontal);
383 float theta = jf * toTheta;
385 float cosTheta = std::cos(theta);
386 float sinTheta = std::sin(theta);
388 thetaCartesian[j] = QVector2D(cosTheta, sinTheta);
389 rhoThetaCartesian[j] = QVector2D(
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);
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);
406 for (
int j = 0; j < lonsp1; ++j)
408 float sTexture = 1.0f - j * toTexHorizontal;
409 sTextureCache[j] = sTexture;
412 int jMod = j % longitudes;
413 QVector2D tc = thetaCartesian[jMod];
414 QVector2D rtc = rhoThetaCartesian[jMod];
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());
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());
430 for (
int i = 0; i < halfLatsn1; ++i)
432 float ip1f = i + 1.0f;
433 float phi = ip1f * toPhi;
436 float cosPhiSouth = std::cos(phi);
437 float sinPhiSouth = std::sin(phi);
441 float cosPhiNorth = sinPhiSouth;
442 float sinPhiNorth = -cosPhiSouth;
444 float rhoCosPhiNorth = radius * cosPhiNorth;
445 float rhoSinPhiNorth = radius * sinPhiNorth;
446 float zOffsetNorth = halfDepth - rhoSinPhiNorth;
448 float rhoCosPhiSouth = radius * cosPhiSouth;
449 float rhoSinPhiSouth = radius * sinPhiSouth;
450 float zOffsetSouth = -halfDepth - rhoSinPhiSouth;
453 float tTexFac = ip1f * toTexVertical;
454 float cmplTexFac = 1.0f - tTexFac;
455 float tTexNorth = cmplTexFac + vtAspectNorth * tTexFac;
456 float tTexSouth = cmplTexFac * vtAspectSouth;
458 int iLonsp1 = i * lonsp1;
459 int vertCurrLatNorth = vertOffsetNorthHemi + iLonsp1;
460 int vertCurrLatSouth = vertOffsetSouthHemi + iLonsp1;
462 for (
int j = 0; j < lonsp1; ++j)
464 int jMod = j % longitudes;
466 float sTexture = sTextureCache[j];
467 QVector2D tc = thetaCartesian[jMod];
470 int idxn = vertCurrLatNorth + j;
471 vs[idxn] = QVector3D(
472 rhoCosPhiNorth * tc.x(),
474 -rhoCosPhiNorth * tc.y());
475 vts[idxn] = QVector2D(sTexture, tTexNorth);
476 vns[idxn] = QVector3D(
477 cosPhiNorth * tc.x(),
479 -cosPhiNorth * tc.y());
482 int idxs = vertCurrLatSouth + j;
483 vs[idxs] = QVector3D(
484 rhoCosPhiSouth * tc.x(),
486 -rhoCosPhiSouth * tc.y());
487 vts[idxs] = QVector2D(sTexture, tTexSouth);
488 vns[idxs] = QVector3D(
489 cosPhiSouth * tc.x(),
491 -cosPhiSouth * tc.y());
500 float toFac = 1.0f / ringsp1;
501 int idxCylLat = vertOffsetCylinder;
503 for (
int h = 1; h < ringsp1; ++h)
505 float fac = h * toFac;
506 float cmplFac = 1.0f - fac;
507 float tTexture = cmplFac * vtAspectNorth + fac * vtAspectSouth;
508 float z = halfDepth - depth * fac;
510 for (
int j = 0; j < lonsp1; ++j)
512 int jMod = j % longitudes;
513 QVector2D tc = thetaCartesian[jMod];
514 QVector2D rtc = rhoThetaCartesian[jMod];
515 float sTexture = sTextureCache[j];
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());
529 int lons3 = longitudes * 3;
530 int lons6 = longitudes * 6;
531 int hemiLons = halfLatsn1 * lons6;
533 int triOffsetNorthHemi = lons3;
534 int triOffsetCylinder = triOffsetNorthHemi + hemiLons;
535 int triOffsetSouthHemi = triOffsetCylinder + ringsp1 * lons6;
536 int triOffsetSouthCap = triOffsetSouthHemi + hemiLons;
538 int fsLen = triOffsetSouthCap + lons3;
539 QList<
int> tris = QList<
int>(fsLen);
542 for (
int i = 0, k = 0, m = triOffsetSouthCap; i < longitudes; ++i, k += 3, m += 3)
546 tris[k + 1] = vertOffsetNorthHemi + i;
547 tris[k + 2] = vertOffsetNorthHemi + i + 1;
550 tris[m] = vertOffsetSouthCap + i;
551 tris[m + 1] = vertOffsetSouthPolar + i + 1;
552 tris[m + 2] = vertOffsetSouthPolar + i;
556 for (
int i = 0, k = triOffsetNorthHemi, m = triOffsetSouthHemi; i < halfLatsn1; ++i)
558 int iLonsp1 = i * lonsp1;
560 int vertCurrLatNorth = vertOffsetNorthHemi + iLonsp1;
561 int vertNextLatNorth = vertCurrLatNorth + lonsp1;
563 int vertCurrLatSouth = vertOffsetSouthEquator + iLonsp1;
564 int vertNextLatSouth = vertCurrLatSouth + lonsp1;
566 for (
int j = 0; j < longitudes; ++j, k += 6, m += 6)
569 int north00 = vertCurrLatNorth + j;
570 int north01 = vertNextLatNorth + j;
571 int north11 = vertNextLatNorth + j + 1;
572 int north10 = vertCurrLatNorth + j + 1;
575 tris[k + 1] = north11;
576 tris[k + 2] = north10;
578 tris[k + 3] = north00;
579 tris[k + 4] = north01;
580 tris[k + 5] = north11;
583 int south00 = vertCurrLatSouth + j;
584 int south01 = vertNextLatSouth + j;
585 int south11 = vertNextLatSouth + j + 1;
586 int south10 = vertCurrLatSouth + j + 1;
589 tris[m + 1] = south11;
590 tris[m + 2] = south10;
592 tris[m + 3] = south00;
593 tris[m + 4] = south01;
594 tris[m + 5] = south11;
599 for (
int i = 0, k = triOffsetCylinder; i < ringsp1; ++i)
601 int vertCurrLat = vertOffsetNorthEquator + i * lonsp1;
602 int vertNextLat = vertCurrLat + lonsp1;
604 for (
int j = 0; j < longitudes; ++j, k += 6)
606 int cy00 = vertCurrLat + j;
607 int cy01 = vertNextLat + j;
608 int cy11 = vertNextLat + j + 1;
609 int cy10 = vertCurrLat + j + 1;
622 auto vertexTextures = vts;
623 auto vertexNormals = vns;
625 uint32_t stride = 3 *
sizeof(
float);
626 uint32_t strideNormal = 0;
627 uint32_t strideUV = 0;
630 strideNormal = stride;
631 stride += 3 *
sizeof(
float);
635 stride += 2 *
sizeof(
float);
638 QByteArray vertexData(vertices.length() * stride, Qt::Initialization::Uninitialized);
639 QByteArray indexData(tris.length() *
sizeof(
int), Qt::Initialization::Uninitialized);
641 const auto getVertexPtr = [&](
const int vertexIdx) {
642 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx);
644 const auto getNormalPtr = [&](
const int vertexIdx) {
645 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx + strideNormal);
647 const auto getTexturePtr = [&](
const int vertexIdx) {
648 return reinterpret_cast<QVector2D *>(vertexData.data() + stride * vertexIdx + strideUV);
651 const QQuaternion rotateZ = QQuaternion::fromEulerAngles(0, 0, 90);
652 uint32_t *indexPtr =
reinterpret_cast<uint32_t *>(indexData.data());
654 for (qsizetype i = 0; i < vertices.length(); i++) {
655 *getVertexPtr(i) = rotateZ * vertices[i];
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];
662 *indexPtr = triIndices[1];
664 *indexPtr = triIndices[2];
668 *getNormalPtr(triIndices[0]) = rotateZ * vertexNormals[triIndices[0]];
669 *getNormalPtr(triIndices[1]) = rotateZ * vertexNormals[triIndices[1]];
670 *getNormalPtr(triIndices[2]) = rotateZ * vertexNormals[triIndices[2]];
674 *getTexturePtr(triIndices[0]) = vertexTextures[triIndices[0]];
675 *getTexturePtr(triIndices[1]) = vertexTextures[triIndices[1]];
676 *getTexturePtr(triIndices[2]) = vertexTextures[triIndices[2]];
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;
693#if QT_CONFIG(concurrent)
694void CapsuleGeometry::generateCapsuleGeometryAsync(QPromise<CapsuleGeometry::GeometryData> &promise,
702 CapsuleGeometry::UVProfile uvProfile)
704 auto output = generateCapsuleGeometry(enableNormals, enableUV, longitudes, latitudes, rings, height, diameter, uvProfile);
705 promise.addResult(output);