9#if QT_CONFIG(concurrent)
10#include <QtConcurrentRun>
19
20
21
22
23
24
25
26
27
28
31
32
33
34
37
38
39
40
43
44
45
46
49
50
51
52
55
56
57
58
61
62
63
64
67
68
69
70
73
74
75
76
77
78
79
80
81
82
83
84
87
88
89
90
91
94
95
96
97
98
99
100
101
102
103
105CapsuleGeometry::CapsuleGeometry(QQuick3DObject *parent) : QQuick3DGeometry(parent)
107#if QT_CONFIG(concurrent)
108 connect(&m_geometryDataWatcher, &QFutureWatcher<GeometryData>::finished,
this, &CapsuleGeometry::requestFinished);
110 scheduleGeometryUpdate();
113CapsuleGeometry::~CapsuleGeometry() =
default;
115void CapsuleGeometry::setEnableNormals(
bool enable)
117 if (m_enableNormals == enable)
120 m_enableNormals = enable;
121 emit enableNormalsChanged();
122 scheduleGeometryUpdate();
125void CapsuleGeometry::setEnableUV(
bool enable)
127 if (m_enableUV == enable)
131 emit enableUVChanged();
132 scheduleGeometryUpdate();
135void CapsuleGeometry::setLongitudes(
int longitudes)
137 if (m_longitudes == longitudes)
140 m_longitudes = longitudes;
141 emit longitudesChanged();
142 scheduleGeometryUpdate();
145void CapsuleGeometry::setLatitudes(
int latitudes)
147 if (m_latitudes == latitudes)
150 m_latitudes = latitudes;
151 emit latitudesChanged();
152 scheduleGeometryUpdate();
155void CapsuleGeometry::setRings(
int rings)
157 if (m_rings == rings)
162 scheduleGeometryUpdate();
165void CapsuleGeometry::setHeight(
float height)
167 if (m_height == height)
171 emit heightChanged();
172 scheduleGeometryUpdate();
175void CapsuleGeometry::setDiameter(
float diameter)
177 if (m_diameter == diameter)
180 m_diameter = diameter;
181 emit diameterChanged();
182 scheduleGeometryUpdate();
185CapsuleGeometry::UVProfile CapsuleGeometry::uvProfile()
const
190void CapsuleGeometry::setUVProfile(UVProfile newUVProfile)
192 if (m_uvProfile == newUVProfile)
194 m_uvProfile = newUVProfile;
195 emit uvProfileChanged();
196 scheduleGeometryUpdate();
199bool CapsuleGeometry::asynchronous()
const
201 return m_asynchronous;
204void CapsuleGeometry::setAsynchronous(
bool newAsynchronous)
206 if (m_asynchronous == newAsynchronous)
208 m_asynchronous = newAsynchronous;
209 emit asynchronousChanged();
212CapsuleGeometry::Status CapsuleGeometry::status()
const
217void CapsuleGeometry::doUpdateGeometry()
220 m_geometryUpdateRequested =
false;
222#if QT_CONFIG(concurrent)
223 if (m_geometryDataFuture.isRunning()) {
224 m_pendingAsyncUpdate =
true;
229#if QT_CONFIG(concurrent)
230 if (m_asynchronous) {
231 m_geometryDataFuture = QtConcurrent::run(generateCapsuleGeometryAsync,
240 m_geometryDataWatcher.setFuture(m_geometryDataFuture);
241 m_status = Status::Loading;
242 Q_EMIT statusChanged();
247 updateGeometry(generateCapsuleGeometry(m_enableNormals, m_enableUV, m_longitudes, m_latitudes, m_rings, m_height, m_diameter, m_uvProfile));
251void CapsuleGeometry::requestFinished()
253#if QT_CONFIG(concurrent)
254 const auto output = m_geometryDataFuture.takeResult();
255 updateGeometry(output);
259void CapsuleGeometry::scheduleGeometryUpdate()
261 if (!m_geometryUpdateRequested) {
262 QMetaObject::invokeMethod(
this,
"doUpdateGeometry", Qt::QueuedConnection);
263 m_geometryUpdateRequested =
true;
267void CapsuleGeometry::updateGeometry(
const GeometryData &geometryData)
270 setStride(geometryData.stride);
271 setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
272 addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::F32Type);
273 if (geometryData.enableNormals) {
274 addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
275 geometryData.strideNormal,
276 QQuick3DGeometry::Attribute::ComponentType::F32Type);
278 if (geometryData.enableUV) {
279 addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic, geometryData.strideUV, QQuick3DGeometry::Attribute::ComponentType::F32Type);
281 addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::U32Type);
283 setBounds(geometryData.boundsMin, geometryData.boundsMax);
284 setVertexData(geometryData.vertexData);
285 setIndexData(geometryData.indexData);
289 if (m_pendingAsyncUpdate) {
290 m_pendingAsyncUpdate =
false;
291 scheduleGeometryUpdate();
293 m_status = Status::Ready;
294 Q_EMIT statusChanged();
299CapsuleGeometry::GeometryData CapsuleGeometry::generateCapsuleGeometry(
bool enableNormals,
308 longitudes = qMax(3, longitudes);
309 latitudes = qMax(2, latitudes + (latitudes % 2));
310 rings = qMax(1, rings);
311 height = qMax(0.00001f, height);
312 diameter = qMax(0.00001f, diameter);
314 const UVProfile profile = uvProfile;
315 const float radius = diameter / 2;
316 const float depth = height;
318 bool calcMiddle = rings > 0;
319 int halfLats = latitudes / 2;
320 int halfLatsn1 = halfLats - 1;
321 int halfLatsn2 = halfLats - 2;
322 int ringsp1 = rings + 1;
323 int lonsp1 = longitudes + 1;
324 float halfDepth = depth * 0.5f;
325 float summit = halfDepth + radius;
328 int vertOffsetNorthHemi = longitudes;
329 int vertOffsetNorthEquator = vertOffsetNorthHemi + lonsp1 * halfLatsn1;
330 int vertOffsetCylinder = vertOffsetNorthEquator + lonsp1;
331 int vertOffsetSouthEquator = calcMiddle ?
332 vertOffsetCylinder + lonsp1 * rings :
334 int vertOffsetSouthHemi = vertOffsetSouthEquator + lonsp1;
335 int vertOffsetSouthPolar = vertOffsetSouthHemi + lonsp1 * halfLatsn2;
336 int vertOffsetSouthCap = vertOffsetSouthPolar + lonsp1;
339 int vertLen = vertOffsetSouthCap + longitudes;
340 QList<QVector3D> vs = QList<QVector3D>(vertLen);
341 QList<QVector2D> vts = QList<QVector2D>(vertLen);
342 QList<QVector3D> vns = QList<QVector3D>(vertLen);
344 float toTheta = 2.0f *
M_PI / longitudes;
345 float toPhi =
M_PI / latitudes;
346 float toTexHorizontal = 1.0f / longitudes;
347 float toTexVertical = 1.0f / halfLats;
350 float vtAspectRatio = 1.0f;
353 case UVProfile::Aspect:
354 vtAspectRatio = radius / (depth + radius + radius);
357 case UVProfile::Uniform:
358 vtAspectRatio = (
float) halfLats / (ringsp1 + latitudes);
361 case UVProfile::Fixed:
363 vtAspectRatio = 1.0f / 3.0f;
367 float vtAspectNorth = 1.0f - vtAspectRatio;
368 float vtAspectSouth = vtAspectRatio;
370 QList<QVector2D> thetaCartesian = QList<QVector2D>(longitudes);
371 QList<QVector2D> rhoThetaCartesian = QList<QVector2D>(longitudes);
372 QList<
float> sTextureCache = QList<
float>(lonsp1);
375 for (
int j = 0; j < longitudes; ++j)
378 float sTexturePolar = 1.0f - ((jf + 0.5f) * toTexHorizontal);
379 float theta = jf * toTheta;
381 float cosTheta = std::cos(theta);
382 float sinTheta = std::sin(theta);
384 thetaCartesian[j] = QVector2D(cosTheta, sinTheta);
385 rhoThetaCartesian[j] = QVector2D(
390 vs[j] = QVector3D(0.0f, summit, 0.0f);
391 vts[j] = QVector2D(sTexturePolar, 1.0f);
392 vns[j] = QVector3D(0.0f, 1.0f, 0.0f);
395 int idx = vertOffsetSouthCap + j;
396 vs[idx] = QVector3D(0.0f, -summit, 0.0f);
397 vts[idx] = QVector2D(sTexturePolar, 0.0f);
398 vns[idx] = QVector3D(0.0f, -1.0f, 0.0f);
402 for (
int j = 0; j < lonsp1; ++j)
404 float sTexture = 1.0f - j * toTexHorizontal;
405 sTextureCache[j] = sTexture;
408 int jMod = j % longitudes;
409 QVector2D tc = thetaCartesian[jMod];
410 QVector2D rtc = rhoThetaCartesian[jMod];
413 int idxn = vertOffsetNorthEquator + j;
414 vs[idxn] = QVector3D(rtc.x(), halfDepth, -rtc.y());
415 vts[idxn] = QVector2D(sTexture, vtAspectNorth);
416 vns[idxn] = QVector3D(tc.x(), 0.0f, -tc.y());
419 int idxs = vertOffsetSouthEquator + j;
420 vs[idxs] = QVector3D(rtc.x(), -halfDepth, -rtc.y());
421 vts[idxs] = QVector2D(sTexture, vtAspectSouth);
422 vns[idxs] = QVector3D(tc.x(), 0.0f, -tc.y());
426 for (
int i = 0; i < halfLatsn1; ++i)
428 float ip1f = i + 1.0f;
429 float phi = ip1f * toPhi;
432 float cosPhiSouth = std::cos(phi);
433 float sinPhiSouth = std::sin(phi);
437 float cosPhiNorth = sinPhiSouth;
438 float sinPhiNorth = -cosPhiSouth;
440 float rhoCosPhiNorth = radius * cosPhiNorth;
441 float rhoSinPhiNorth = radius * sinPhiNorth;
442 float zOffsetNorth = halfDepth - rhoSinPhiNorth;
444 float rhoCosPhiSouth = radius * cosPhiSouth;
445 float rhoSinPhiSouth = radius * sinPhiSouth;
446 float zOffsetSouth = -halfDepth - rhoSinPhiSouth;
449 float tTexFac = ip1f * toTexVertical;
450 float cmplTexFac = 1.0f - tTexFac;
451 float tTexNorth = cmplTexFac + vtAspectNorth * tTexFac;
452 float tTexSouth = cmplTexFac * vtAspectSouth;
454 int iLonsp1 = i * lonsp1;
455 int vertCurrLatNorth = vertOffsetNorthHemi + iLonsp1;
456 int vertCurrLatSouth = vertOffsetSouthHemi + iLonsp1;
458 for (
int j = 0; j < lonsp1; ++j)
460 int jMod = j % longitudes;
462 float sTexture = sTextureCache[j];
463 QVector2D tc = thetaCartesian[jMod];
466 int idxn = vertCurrLatNorth + j;
467 vs[idxn] = QVector3D(
468 rhoCosPhiNorth * tc.x(),
470 -rhoCosPhiNorth * tc.y());
471 vts[idxn] = QVector2D(sTexture, tTexNorth);
472 vns[idxn] = QVector3D(
473 cosPhiNorth * tc.x(),
475 -cosPhiNorth * tc.y());
478 int idxs = vertCurrLatSouth + j;
479 vs[idxs] = QVector3D(
480 rhoCosPhiSouth * tc.x(),
482 -rhoCosPhiSouth * tc.y());
483 vts[idxs] = QVector2D(sTexture, tTexSouth);
484 vns[idxs] = QVector3D(
485 cosPhiSouth * tc.x(),
487 -cosPhiSouth * tc.y());
496 float toFac = 1.0f / ringsp1;
497 int idxCylLat = vertOffsetCylinder;
499 for (
int h = 1; h < ringsp1; ++h)
501 float fac = h * toFac;
502 float cmplFac = 1.0f - fac;
503 float tTexture = cmplFac * vtAspectNorth + fac * vtAspectSouth;
504 float z = halfDepth - depth * fac;
506 for (
int j = 0; j < lonsp1; ++j)
508 int jMod = j % longitudes;
509 QVector2D tc = thetaCartesian[jMod];
510 QVector2D rtc = rhoThetaCartesian[jMod];
511 float sTexture = sTextureCache[j];
513 vs[idxCylLat] = QVector3D(rtc.x(), z, -rtc.y());
514 vts[idxCylLat] = QVector2D(sTexture, tTexture);
515 vns[idxCylLat] = QVector3D(tc.x(), 0.0f, -tc.y());
525 int lons3 = longitudes * 3;
526 int lons6 = longitudes * 6;
527 int hemiLons = halfLatsn1 * lons6;
529 int triOffsetNorthHemi = lons3;
530 int triOffsetCylinder = triOffsetNorthHemi + hemiLons;
531 int triOffsetSouthHemi = triOffsetCylinder + ringsp1 * lons6;
532 int triOffsetSouthCap = triOffsetSouthHemi + hemiLons;
534 int fsLen = triOffsetSouthCap + lons3;
535 QList<
int> tris = QList<
int>(fsLen);
538 for (
int i = 0, k = 0, m = triOffsetSouthCap; i < longitudes; ++i, k += 3, m += 3)
542 tris[k + 1] = vertOffsetNorthHemi + i;
543 tris[k + 2] = vertOffsetNorthHemi + i + 1;
546 tris[m] = vertOffsetSouthCap + i;
547 tris[m + 1] = vertOffsetSouthPolar + i + 1;
548 tris[m + 2] = vertOffsetSouthPolar + i;
552 for (
int i = 0, k = triOffsetNorthHemi, m = triOffsetSouthHemi; i < halfLatsn1; ++i)
554 int iLonsp1 = i * lonsp1;
556 int vertCurrLatNorth = vertOffsetNorthHemi + iLonsp1;
557 int vertNextLatNorth = vertCurrLatNorth + lonsp1;
559 int vertCurrLatSouth = vertOffsetSouthEquator + iLonsp1;
560 int vertNextLatSouth = vertCurrLatSouth + lonsp1;
562 for (
int j = 0; j < longitudes; ++j, k += 6, m += 6)
565 int north00 = vertCurrLatNorth + j;
566 int north01 = vertNextLatNorth + j;
567 int north11 = vertNextLatNorth + j + 1;
568 int north10 = vertCurrLatNorth + j + 1;
571 tris[k + 1] = north11;
572 tris[k + 2] = north10;
574 tris[k + 3] = north00;
575 tris[k + 4] = north01;
576 tris[k + 5] = north11;
579 int south00 = vertCurrLatSouth + j;
580 int south01 = vertNextLatSouth + j;
581 int south11 = vertNextLatSouth + j + 1;
582 int south10 = vertCurrLatSouth + j + 1;
585 tris[m + 1] = south11;
586 tris[m + 2] = south10;
588 tris[m + 3] = south00;
589 tris[m + 4] = south01;
590 tris[m + 5] = south11;
595 for (
int i = 0, k = triOffsetCylinder; i < ringsp1; ++i)
597 int vertCurrLat = vertOffsetNorthEquator + i * lonsp1;
598 int vertNextLat = vertCurrLat + lonsp1;
600 for (
int j = 0; j < longitudes; ++j, k += 6)
602 int cy00 = vertCurrLat + j;
603 int cy01 = vertNextLat + j;
604 int cy11 = vertNextLat + j + 1;
605 int cy10 = vertCurrLat + j + 1;
618 auto vertexTextures = vts;
619 auto vertexNormals = vns;
621 uint32_t stride = 3 *
sizeof(
float);
622 uint32_t strideNormal = 0;
623 uint32_t strideUV = 0;
626 strideNormal = stride;
627 stride += 3 *
sizeof(
float);
631 stride += 2 *
sizeof(
float);
634 QByteArray vertexData(vertices.length() * stride, Qt::Initialization::Uninitialized);
635 QByteArray indexData(tris.length() *
sizeof(
int), Qt::Initialization::Uninitialized);
637 const auto getVertexPtr = [&](
const int vertexIdx) {
638 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx);
640 const auto getNormalPtr = [&](
const int vertexIdx) {
641 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx + strideNormal);
643 const auto getTexturePtr = [&](
const int vertexIdx) {
644 return reinterpret_cast<QVector2D *>(vertexData.data() + stride * vertexIdx + strideUV);
647 const QQuaternion rotateZ = QQuaternion::fromEulerAngles(0, 0, 90);
648 uint32_t *indexPtr =
reinterpret_cast<uint32_t *>(indexData.data());
650 for (qsizetype i = 0; i < vertices.length(); i++) {
651 *getVertexPtr(i) = rotateZ * vertices[i];
654 for (qsizetype i = 0; i < tris.length() / 3; i++) {
655 std::array<
int, 3> triIndices = { tris[i * 3], tris[i * 3 + 1], tris[i * 3 + 2] };
656 *indexPtr = triIndices[0];
658 *indexPtr = triIndices[1];
660 *indexPtr = triIndices[2];
664 *getNormalPtr(triIndices[0]) = rotateZ * vertexNormals[triIndices[0]];
665 *getNormalPtr(triIndices[1]) = rotateZ * vertexNormals[triIndices[1]];
666 *getNormalPtr(triIndices[2]) = rotateZ * vertexNormals[triIndices[2]];
670 *getTexturePtr(triIndices[0]) = vertexTextures[triIndices[0]];
671 *getTexturePtr(triIndices[1]) = vertexTextures[triIndices[1]];
672 *getTexturePtr(triIndices[2]) = vertexTextures[triIndices[2]];
676 GeometryData geometryData;
677 geometryData.indexData = indexData;
678 geometryData.vertexData = vertexData;
679 geometryData.boundsMin = QVector3D(-radius - halfDepth, -radius, -radius);
680 geometryData.boundsMax = QVector3D(radius + halfDepth, radius, radius);
681 geometryData.stride = stride;
682 geometryData.strideNormal = strideNormal;
683 geometryData.strideUV = strideUV;
684 geometryData.enableNormals = enableNormals;
685 geometryData.enableUV = enableUV;
689#if QT_CONFIG(concurrent)
690void CapsuleGeometry::generateCapsuleGeometryAsync(QPromise<CapsuleGeometry::GeometryData> &promise,
698 CapsuleGeometry::UVProfile uvProfile)
700 auto output = generateCapsuleGeometry(enableNormals, enableUV, longitudes, latitudes, rings, height, diameter, uvProfile);
701 promise.addResult(output);