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)
232 if (m_asynchronous) {
233 m_geometryDataFuture = QtConcurrent::run(generateCapsuleGeometryAsync,
242 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
243 m_status = Status::Loading;
244 Q_EMIT statusChanged();
249 updateGeometry(generateCapsuleGeometry(m_enableNormals, m_enableUV, m_longitudes, m_latitudes, m_rings, m_height, m_diameter, m_uvProfile));
253void CapsuleGeometry::requestFinished()
255#if QT_CONFIG(concurrent)
256 const auto output = m_geometryDataFuture.takeResult();
257 updateGeometry(output);
261void CapsuleGeometry::scheduleGeometryUpdate()
263 if (!m_geometryUpdateRequested) {
264 QMetaObject::invokeMethod(
this,
"doUpdateGeometry", Qt::QueuedConnection);
265 m_geometryUpdateRequested =
true;
269void CapsuleGeometry::updateGeometry(
const GeometryData &geometryData)
272 setStride(geometryData.stride);
273 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
274 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::F32Type);
275 if (geometryData.enableNormals) {
276 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
277 geometryData.strideNormal,
278 QQuick3DGeometry::Attribute::ComponentType::F32Type);
280 if (geometryData.enableUV) {
281 addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic, geometryData.strideUV, QQuick3DGeometry::Attribute::ComponentType::F32Type);
283 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::U32Type);
285 setBounds(geometryData.boundsMin, geometryData.boundsMax);
286 setVertexData(geometryData.vertexData);
287 setIndexData(geometryData.indexData);
291 if (m_pendingAsyncUpdate) {
292 m_pendingAsyncUpdate =
false;
293 scheduleGeometryUpdate();
295 m_status = Status::Ready;
296 Q_EMIT statusChanged();
299 emit geometryChanged();
302CapsuleGeometry::GeometryData CapsuleGeometry::generateCapsuleGeometry(
bool enableNormals,
311 longitudes = qMax(3, longitudes);
312 latitudes = qMax(2, latitudes + (latitudes % 2));
313 rings = qMax(1, rings);
314 height = qMax(0.00001f, height);
315 diameter = qMax(0.00001f, diameter);
317 const UVProfile profile = uvProfile;
318 const float radius = diameter / 2;
319 const float depth = height;
321 bool calcMiddle = rings > 0;
322 int halfLats = latitudes / 2;
323 int halfLatsn1 = halfLats - 1;
324 int halfLatsn2 = halfLats - 2;
325 int ringsp1 = rings + 1;
326 int lonsp1 = longitudes + 1;
327 float halfDepth = depth * 0.5f;
328 float summit = halfDepth + radius;
331 int vertOffsetNorthHemi = longitudes;
332 int vertOffsetNorthEquator = vertOffsetNorthHemi + lonsp1 * halfLatsn1;
333 int vertOffsetCylinder = vertOffsetNorthEquator + lonsp1;
334 int vertOffsetSouthEquator = calcMiddle ?
335 vertOffsetCylinder + lonsp1 * rings :
337 int vertOffsetSouthHemi = vertOffsetSouthEquator + lonsp1;
338 int vertOffsetSouthPolar = vertOffsetSouthHemi + lonsp1 * halfLatsn2;
339 int vertOffsetSouthCap = vertOffsetSouthPolar + lonsp1;
342 int vertLen = vertOffsetSouthCap + longitudes;
343 QList<QVector3D> vs = QList<QVector3D>(vertLen);
344 QList<QVector2D> vts = QList<QVector2D>(vertLen);
345 QList<QVector3D> vns = QList<QVector3D>(vertLen);
347 float toTheta = 2.0f *
M_PI / longitudes;
348 float toPhi =
M_PI / latitudes;
349 float toTexHorizontal = 1.0f / longitudes;
350 float toTexVertical = 1.0f / halfLats;
353 float vtAspectRatio = 1.0f;
356 case UVProfile::Aspect:
357 vtAspectRatio = radius / (depth + radius + radius);
360 case UVProfile::Uniform:
361 vtAspectRatio = (
float) halfLats / (ringsp1 + latitudes);
364 case UVProfile::Fixed:
366 vtAspectRatio = 1.0f / 3.0f;
370 float vtAspectNorth = 1.0f - vtAspectRatio;
371 float vtAspectSouth = vtAspectRatio;
373 QList<QVector2D> thetaCartesian = QList<QVector2D>(longitudes);
374 QList<QVector2D> rhoThetaCartesian = QList<QVector2D>(longitudes);
375 QList<
float> sTextureCache = QList<
float>(lonsp1);
378 for (
int j = 0; j < longitudes; ++j)
381 float sTexturePolar = 1.0f - ((jf + 0.5f) * toTexHorizontal);
382 float theta = jf * toTheta;
384 float cosTheta = std::cos(theta);
385 float sinTheta = std::sin(theta);
387 thetaCartesian[j] = QVector2D(cosTheta, sinTheta);
388 rhoThetaCartesian[j] = QVector2D(
393 vs[j] = QVector3D(0.0f, summit, 0.0f);
394 vts[j] = QVector2D(sTexturePolar, 1.0f);
395 vns[j] = QVector3D(0.0f, 1.0f, 0.0f);
398 int idx = vertOffsetSouthCap + j;
399 vs[idx] = QVector3D(0.0f, -summit, 0.0f);
400 vts[idx] = QVector2D(sTexturePolar, 0.0f);
401 vns[idx] = QVector3D(0.0f, -1.0f, 0.0f);
405 for (
int j = 0; j < lonsp1; ++j)
407 float sTexture = 1.0f - j * toTexHorizontal;
408 sTextureCache[j] = sTexture;
411 int jMod = j % longitudes;
412 QVector2D tc = thetaCartesian[jMod];
413 QVector2D rtc = rhoThetaCartesian[jMod];
416 int idxn = vertOffsetNorthEquator + j;
417 vs[idxn] = QVector3D(rtc.x(), halfDepth, -rtc.y());
418 vts[idxn] = QVector2D(sTexture, vtAspectNorth);
419 vns[idxn] = QVector3D(tc.x(), 0.0f, -tc.y());
422 int idxs = vertOffsetSouthEquator + j;
423 vs[idxs] = QVector3D(rtc.x(), -halfDepth, -rtc.y());
424 vts[idxs] = QVector2D(sTexture, vtAspectSouth);
425 vns[idxs] = QVector3D(tc.x(), 0.0f, -tc.y());
429 for (
int i = 0; i < halfLatsn1; ++i)
431 float ip1f = i + 1.0f;
432 float phi = ip1f * toPhi;
435 float cosPhiSouth = std::cos(phi);
436 float sinPhiSouth = std::sin(phi);
440 float cosPhiNorth = sinPhiSouth;
441 float sinPhiNorth = -cosPhiSouth;
443 float rhoCosPhiNorth = radius * cosPhiNorth;
444 float rhoSinPhiNorth = radius * sinPhiNorth;
445 float zOffsetNorth = halfDepth - rhoSinPhiNorth;
447 float rhoCosPhiSouth = radius * cosPhiSouth;
448 float rhoSinPhiSouth = radius * sinPhiSouth;
449 float zOffsetSouth = -halfDepth - rhoSinPhiSouth;
452 float tTexFac = ip1f * toTexVertical;
453 float cmplTexFac = 1.0f - tTexFac;
454 float tTexNorth = cmplTexFac + vtAspectNorth * tTexFac;
455 float tTexSouth = cmplTexFac * vtAspectSouth;
457 int iLonsp1 = i * lonsp1;
458 int vertCurrLatNorth = vertOffsetNorthHemi + iLonsp1;
459 int vertCurrLatSouth = vertOffsetSouthHemi + iLonsp1;
461 for (
int j = 0; j < lonsp1; ++j)
463 int jMod = j % longitudes;
465 float sTexture = sTextureCache[j];
466 QVector2D tc = thetaCartesian[jMod];
469 int idxn = vertCurrLatNorth + j;
470 vs[idxn] = QVector3D(
471 rhoCosPhiNorth * tc.x(),
473 -rhoCosPhiNorth * tc.y());
474 vts[idxn] = QVector2D(sTexture, tTexNorth);
475 vns[idxn] = QVector3D(
476 cosPhiNorth * tc.x(),
478 -cosPhiNorth * tc.y());
481 int idxs = vertCurrLatSouth + j;
482 vs[idxs] = QVector3D(
483 rhoCosPhiSouth * tc.x(),
485 -rhoCosPhiSouth * tc.y());
486 vts[idxs] = QVector2D(sTexture, tTexSouth);
487 vns[idxs] = QVector3D(
488 cosPhiSouth * tc.x(),
490 -cosPhiSouth * tc.y());
499 float toFac = 1.0f / ringsp1;
500 int idxCylLat = vertOffsetCylinder;
502 for (
int h = 1; h < ringsp1; ++h)
504 float fac = h * toFac;
505 float cmplFac = 1.0f - fac;
506 float tTexture = cmplFac * vtAspectNorth + fac * vtAspectSouth;
507 float z = halfDepth - depth * fac;
509 for (
int j = 0; j < lonsp1; ++j)
511 int jMod = j % longitudes;
512 QVector2D tc = thetaCartesian[jMod];
513 QVector2D rtc = rhoThetaCartesian[jMod];
514 float sTexture = sTextureCache[j];
516 vs[idxCylLat] = QVector3D(rtc.x(), z, -rtc.y());
517 vts[idxCylLat] = QVector2D(sTexture, tTexture);
518 vns[idxCylLat] = QVector3D(tc.x(), 0.0f, -tc.y());
528 int lons3 = longitudes * 3;
529 int lons6 = longitudes * 6;
530 int hemiLons = halfLatsn1 * lons6;
532 int triOffsetNorthHemi = lons3;
533 int triOffsetCylinder = triOffsetNorthHemi + hemiLons;
534 int triOffsetSouthHemi = triOffsetCylinder + ringsp1 * lons6;
535 int triOffsetSouthCap = triOffsetSouthHemi + hemiLons;
537 int fsLen = triOffsetSouthCap + lons3;
538 QList<
int> tris = QList<
int>(fsLen);
541 for (
int i = 0, k = 0, m = triOffsetSouthCap; i < longitudes; ++i, k += 3, m += 3)
545 tris[k + 1] = vertOffsetNorthHemi + i;
546 tris[k + 2] = vertOffsetNorthHemi + i + 1;
549 tris[m] = vertOffsetSouthCap + i;
550 tris[m + 1] = vertOffsetSouthPolar + i + 1;
551 tris[m + 2] = vertOffsetSouthPolar + i;
555 for (
int i = 0, k = triOffsetNorthHemi, m = triOffsetSouthHemi; i < halfLatsn1; ++i)
557 int iLonsp1 = i * lonsp1;
559 int vertCurrLatNorth = vertOffsetNorthHemi + iLonsp1;
560 int vertNextLatNorth = vertCurrLatNorth + lonsp1;
562 int vertCurrLatSouth = vertOffsetSouthEquator + iLonsp1;
563 int vertNextLatSouth = vertCurrLatSouth + lonsp1;
565 for (
int j = 0; j < longitudes; ++j, k += 6, m += 6)
568 int north00 = vertCurrLatNorth + j;
569 int north01 = vertNextLatNorth + j;
570 int north11 = vertNextLatNorth + j + 1;
571 int north10 = vertCurrLatNorth + j + 1;
574 tris[k + 1] = north11;
575 tris[k + 2] = north10;
577 tris[k + 3] = north00;
578 tris[k + 4] = north01;
579 tris[k + 5] = north11;
582 int south00 = vertCurrLatSouth + j;
583 int south01 = vertNextLatSouth + j;
584 int south11 = vertNextLatSouth + j + 1;
585 int south10 = vertCurrLatSouth + j + 1;
588 tris[m + 1] = south11;
589 tris[m + 2] = south10;
591 tris[m + 3] = south00;
592 tris[m + 4] = south01;
593 tris[m + 5] = south11;
598 for (
int i = 0, k = triOffsetCylinder; i < ringsp1; ++i)
600 int vertCurrLat = vertOffsetNorthEquator + i * lonsp1;
601 int vertNextLat = vertCurrLat + lonsp1;
603 for (
int j = 0; j < longitudes; ++j, k += 6)
605 int cy00 = vertCurrLat + j;
606 int cy01 = vertNextLat + j;
607 int cy11 = vertNextLat + j + 1;
608 int cy10 = vertCurrLat + j + 1;
621 auto vertexTextures = vts;
622 auto vertexNormals = vns;
624 uint32_t stride = 3 *
sizeof(
float);
625 uint32_t strideNormal = 0;
626 uint32_t strideUV = 0;
629 strideNormal = stride;
630 stride += 3 *
sizeof(
float);
634 stride += 2 *
sizeof(
float);
637 QByteArray vertexData(vertices.length() * stride, Qt::Initialization::Uninitialized);
638 QByteArray indexData(tris.length() *
sizeof(
int), Qt::Initialization::Uninitialized);
640 const auto getVertexPtr = [&](
const int vertexIdx) {
641 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx);
643 const auto getNormalPtr = [&](
const int vertexIdx) {
644 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx + strideNormal);
646 const auto getTexturePtr = [&](
const int vertexIdx) {
647 return reinterpret_cast<QVector2D *>(vertexData.data() + stride * vertexIdx + strideUV);
650 const QQuaternion rotateZ = QQuaternion::fromEulerAngles(0, 0, 90);
651 uint32_t *indexPtr =
reinterpret_cast<uint32_t *>(indexData.data());
653 for (qsizetype i = 0; i < vertices.length(); i++) {
654 *getVertexPtr(i) = rotateZ * vertices[i];
657 for (qsizetype i = 0; i < tris.length() / 3; i++) {
658 std::array<
int, 3> triIndices = { tris[i * 3], tris[i * 3 + 1], tris[i * 3 + 2] };
659 *indexPtr = triIndices[0];
661 *indexPtr = triIndices[1];
663 *indexPtr = triIndices[2];
667 *getNormalPtr(triIndices[0]) = rotateZ * vertexNormals[triIndices[0]];
668 *getNormalPtr(triIndices[1]) = rotateZ * vertexNormals[triIndices[1]];
669 *getNormalPtr(triIndices[2]) = rotateZ * vertexNormals[triIndices[2]];
673 *getTexturePtr(triIndices[0]) = vertexTextures[triIndices[0]];
674 *getTexturePtr(triIndices[1]) = vertexTextures[triIndices[1]];
675 *getTexturePtr(triIndices[2]) = vertexTextures[triIndices[2]];
679 GeometryData geometryData;
680 geometryData.indexData = indexData;
681 geometryData.vertexData = vertexData;
682 geometryData.boundsMin = QVector3D(-radius - halfDepth, -radius, -radius);
683 geometryData.boundsMax = QVector3D(radius + halfDepth, radius, radius);
684 geometryData.stride = stride;
685 geometryData.strideNormal = strideNormal;
686 geometryData.strideUV = strideUV;
687 geometryData.enableNormals = enableNormals;
688 geometryData.enableUV = enableUV;
692#if QT_CONFIG(concurrent)
693void CapsuleGeometry::generateCapsuleGeometryAsync(QPromise<CapsuleGeometry::GeometryData> &promise,
701 CapsuleGeometry::UVProfile uvProfile)
703 auto output = generateCapsuleGeometry(enableNormals, enableUV, longitudes, latitudes, rings, height, diameter, uvProfile);
704 promise.addResult(output);