1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4// Based on:
5// https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db
6
7#include "qcapsulegeometry_p.h"
8
9#include <QVector3D>
10
11QT_BEGIN_NAMESPACE
12
13CapsuleGeometry::CapsuleGeometry()
14{
15 updateData();
16}
17
18void CapsuleGeometry::setEnableNormals(bool enable)
19{
20 if (m_enableNormals == enable)
21 return;
22
23 m_enableNormals = enable;
24 emit enableNormalsChanged();
25 updateData();
26 update();
27}
28
29void CapsuleGeometry::setEnableUV(bool enable)
30{
31 if (m_enableUV == enable)
32 return;
33
34 m_enableUV = enable;
35 emit enableUVChanged();
36 updateData();
37 update();
38}
39
40void CapsuleGeometry::setLongitudes(int longitudes)
41{
42 if (m_longitudes == longitudes)
43 return;
44
45 m_longitudes = longitudes;
46 emit longitudesChanged();
47 updateData();
48 update();
49}
50
51void CapsuleGeometry::setLatitudes(int latitudes)
52{
53 if (m_latitudes == latitudes)
54 return;
55
56 m_latitudes = latitudes;
57 emit latitudesChanged();
58 updateData();
59 update();
60}
61
62void CapsuleGeometry::setRings(int rings)
63{
64 if (m_rings == rings)
65 return;
66
67 m_rings = rings;
68 emit ringsChanged();
69 updateData();
70 update();
71}
72
73void CapsuleGeometry::setHeight(float height)
74{
75 if (m_height == height)
76 return;
77
78 m_height = height;
79 emit heightChanged();
80 updateData();
81 update();
82}
83
84void CapsuleGeometry::setDiameter(float diameter)
85{
86 if (m_diameter == diameter)
87 return;
88
89 m_diameter = diameter;
90 emit diameterChanged();
91 updateData();
92 update();
93}
94
95struct Face
96{
97 // Coordinate index.
98 uint32_t vertexIdx = 0;
99 // Texture coordinate index.
100 uint32_t textureIdx = 0;
101 // Normal index.
102 uint32_t normalIdx = 0;
103};
104
105void CapsuleGeometry::updateData()
106{
107 clear();
108
109 constexpr float EPSILON = 0.001f;
110 const float radius = m_diameter * 0.5f;
111
112 // m_latitudes must be even for symmetry.
113 int verifLats = qMax(a: 2, b: m_latitudes);
114 if (verifLats % 2 != 0) {
115 verifLats += 1;
116 }
117
118 // Validate input arguments.
119 uint32_t verifLons = qMax(a: 3, b: m_longitudes);
120 uint32_t verifRings = qMax(a: 0, b: m_rings);
121 float verifDepth = qMax(a: EPSILON, b: m_height);
122 float verifRad = qMax(a: EPSILON, b: radius);
123
124 // Intermediary calculations.
125 bool calcMiddle = verifRings > 0;
126 uint32_t halfLats = verifLats / 2;
127 uint32_t halfLatsn1 = halfLats - 1;
128 uint32_t halfLatsn2 = halfLats - 2;
129 uint32_t verifRingsp1 = verifRings + 1;
130 uint32_t verifLonsp1 = verifLons + 1;
131 uint32_t lonsHalfLatn1 = halfLatsn1 * verifLons;
132 uint32_t lonsRingsp1 = verifRingsp1 * verifLons;
133 float halfDepth = verifDepth * 0.5f;
134 float summit = halfDepth + verifRad;
135
136 // Index offsets for coordinates.
137 uint32_t idxVNEquator = verifLonsp1 + verifLons * halfLatsn2;
138 uint32_t idxVCyl = idxVNEquator + verifLons;
139 uint32_t idxVSEquator = idxVCyl;
140 if (calcMiddle) {
141 idxVSEquator += verifLons * verifRings;
142 }
143 uint32_t idxVSouth = idxVSEquator + verifLons;
144 uint32_t idxVSouthCap = idxVSouth + verifLons * halfLatsn2;
145 uint32_t idxVSouthPole = idxVSouthCap + verifLons;
146
147 // Index offsets for texture coordinates.
148 uint32_t idxVtNEquator = verifLons + verifLonsp1 * halfLatsn1;
149 uint32_t idxVtCyl = idxVtNEquator + verifLonsp1;
150 uint32_t idxVtSEquator = idxVtCyl;
151 if (calcMiddle) {
152 idxVtSEquator += verifLonsp1 * verifRings;
153 }
154 uint32_t idxVtSHemi = idxVtSEquator + verifLonsp1;
155 uint32_t idxVtSPolar = idxVtSHemi + verifLonsp1 * halfLatsn2;
156 uint32_t idxVtSCap = idxVtSPolar + verifLonsp1;
157
158 // Index offsets for normals.
159 uint32_t idxVnSouth = idxVNEquator + verifLons;
160 uint32_t idxVnSouthCap = idxVnSouth + verifLons * halfLatsn2;
161 uint32_t idxVnSouthPole = idxVnSouthCap + verifLons;
162
163 // Find index offsets for face indices.
164 uint32_t idxFsCyl = verifLons + lonsHalfLatn1 * 2;
165 uint32_t idxFsSouthEquat = idxFsCyl + lonsRingsp1 * 2;
166 uint32_t idxFsSouthHemi = idxFsSouthEquat + lonsHalfLatn1 * 2;
167
168 // Array lengths.
169 uint32_t verticesLen = idxVSouthPole + 1;
170 uint32_t texturesLen = idxVtSCap + verifLons;
171 uint32_t normalsLen = idxVnSouthPole + 1;
172 uint32_t facesLen = idxFsSouthHemi + verifLons;
173
174 // Initialize arrays.
175 auto vertices = QList<QVector3D>(verticesLen);
176 auto vertexTextures = QList<QVector2D>(texturesLen);
177 auto vertexNormals = QList<QVector3D>(normalsLen);
178
179 // If we plan to use only triangles, we can initialize
180 // the inner array to 3.
181 auto faces = QList<std::array<Face, 3>>(facesLen);
182
183 // North pole.
184 vertices[0] = QVector3D(-summit, 0.f, 0.f);
185 vertexNormals[0] = QVector3D(-1.f, 0.f, 0.f);
186
187 // South pole.
188 vertices[idxVSouthPole] = QVector3D(summit, 0.f, 0.f);
189 vertexNormals[idxVnSouthPole] = QVector3D(1.f, 0.f, 0.f);
190
191 // Calculate polar texture coordinates, equatorial coordinates.
192 QList<float> sinThetaCache = QList<float>(verifLons);
193 QList<float> cosThetaCache = QList<float>(verifLons);
194 float toTheta = 2 * M_PI / verifLons;
195 float toPhi = M_PI / verifLats;
196 float toTexHorizontal = 1.f / verifLons;
197 float toTexVertical = 1.f / halfLats;
198
199 for (uint32_t j = 0; j < verifLons; ++j) {
200
201 // Coordinates.
202 float theta = j * toTheta;
203 float sinTheta = sin(x: theta);
204 float cosTheta = cos(x: theta);
205 sinThetaCache[j] = sinTheta;
206 cosThetaCache[j] = cosTheta;
207
208 // Texture coordinates at North and South pole.
209 float sTex = (j + 0.5f) * toTexHorizontal;
210 vertexTextures[j] = QVector2D(sTex, 1.f);
211 vertexTextures[idxVtSCap + j] = QVector2D(sTex, 0.f);
212
213 // Multiply by radius to get equatorial x and y.
214 float x = verifRad * cosTheta;
215 float z = verifRad * sinTheta;
216
217 // Set equatorial coordinates. Offset by cylinder depth.
218 vertices[idxVNEquator + j] = QVector3D(-halfDepth, x, -z);
219 vertices[idxVSEquator + j] = QVector3D(halfDepth, x, -z);
220
221 // Set equatorial normals.
222 vertexNormals[idxVNEquator + j] = QVector3D(0.f, cosTheta, -sinTheta);
223
224 // Set polar indices.
225 uint32_t jNextVt = j + 1;
226 uint32_t jNextV = jNextVt % verifLons;
227
228 // North triangle.
229 faces[j] = { Face { .vertexIdx: 0, .textureIdx: j, .normalIdx: 0 }, Face { .vertexIdx: jNextVt, .textureIdx: verifLons + j, .normalIdx: jNextVt },
230 Face { .vertexIdx: 1 + jNextV, .textureIdx: verifLons + jNextVt, .normalIdx: 1 + jNextV } };
231
232 // South triangle.
233 faces[idxFsSouthHemi + j] = {
234 Face { .vertexIdx: idxVSouthPole, .textureIdx: idxVtSCap + j, .normalIdx: idxVnSouthPole },
235 Face { .vertexIdx: idxVSouthCap + jNextV, .textureIdx: idxVtSPolar + jNextVt, .normalIdx: idxVnSouthCap + jNextV },
236 Face { .vertexIdx: idxVSouthCap + j, .textureIdx: idxVtSPolar + j, .normalIdx: idxVnSouthCap + j }
237 };
238 }
239
240 // Determine UV aspect ratio from the profile.
241 float vtAspectRatio = 0.f;
242 switch (m_uvProfile) {
243 case CapsuleGeometry::UvProfile::Fixed:
244 vtAspectRatio = 0.33333333f;
245 break;
246 case CapsuleGeometry::UvProfile::Aspect:
247 vtAspectRatio = verifRad / (verifDepth + verifRad + verifRad);
248 break;
249 case CapsuleGeometry::UvProfile::Uniform:
250 vtAspectRatio = (float)halfLats / (verifRingsp1 + verifLats);
251 break;
252 }
253 float vtAspectSouth = vtAspectRatio;
254 float vtAspectNorth = 1.f - vtAspectRatio;
255
256 // Cache horizontal measure.
257 QList<float> sTexCache = QList<float>(verifLonsp1);
258
259 // Calculate equatorial texture coordinates.
260 for (uint32_t j = 0; j < verifLonsp1; ++j) {
261 float sTex = j * toTexHorizontal;
262 sTexCache[j] = sTex;
263 vertexTextures[idxVtNEquator + j] = QVector2D(sTex, vtAspectNorth);
264 vertexTextures[idxVtSEquator + j] = QVector2D(sTex, vtAspectSouth);
265 }
266
267 // Divide m_latitudes into hemispheres. Start at i = 1 due to the poles.
268 uint32_t vHemiOffsetNorth = 1;
269 uint32_t vHemiOffsetSouth = idxVSouth;
270 uint32_t vtHemiOffsetNorth = verifLons;
271 uint32_t vtHemiOffsetSouth = idxVtSHemi;
272 uint32_t vnHemiOffsetSouth = idxVnSouth;
273 uint32_t fHemiOffsetNorth = verifLons;
274 uint32_t fHemiOffsetSouth = idxFsSouthEquat;
275
276 for (uint32_t i = 0; i < halfLatsn1; ++i) {
277 uint32_t iLonsCurr = i * verifLons;
278 float ip1f = i + 1.f;
279 float phi = ip1f * toPhi;
280 float sinPhiSouth = sin(x: phi);
281 float cosPhiSouth = cos(x: phi);
282
283 // Use trigonometric symmetries to avoid calculating another
284 // sine and cosine for phi North.
285 float cosPhiNorth = sinPhiSouth;
286 float sinPhiNorth = -cosPhiSouth;
287
288 // For North coordinates, multiply by radius and offset.
289 float rhoCosPhiNorth = verifRad * cosPhiNorth;
290 float rhoSinPhiNorth = verifRad * sinPhiNorth;
291 float yOffsetNorth = halfDepth - rhoSinPhiNorth;
292
293 // For South coordinates, multiply by radius and offset.
294 float rhoCosPhiSouth = verifRad * cosPhiSouth;
295 float rhoSinPhiSouth = verifRad * sinPhiSouth;
296 float yOffsetSouth = -halfDepth - rhoSinPhiSouth;
297
298 // North coordinate index offset.
299 uint32_t vCurrLatN = 1 + iLonsCurr;
300 uint32_t vNextLatN = vCurrLatN + verifLons;
301
302 // South coordinate index offset.
303 uint32_t vCurrLatS = idxVSEquator + iLonsCurr;
304 uint32_t vNextLatS = vCurrLatS + verifLons;
305
306 // North texture coordinate index offset.
307 uint32_t vtCurrLatN = verifLons + i * verifLonsp1;
308 uint32_t vtNextLatN = vtCurrLatN + verifLonsp1;
309
310 // South texture coordinate index offset.
311 uint32_t vtCurrLatS = idxVtSEquator + i * verifLonsp1;
312 uint32_t vtNextLatS = vtCurrLatS + verifLonsp1;
313
314 // North normal index offset.
315 uint32_t vnCurrLatN = 1 + iLonsCurr;
316 uint32_t vnNextLatN = vnCurrLatN + verifLons;
317
318 // South normal index offset.
319 uint32_t vnCurrLatS = idxVNEquator + iLonsCurr;
320 uint32_t vnNextLatS = vnCurrLatS + verifLons;
321
322 // Coordinates, normals and face indices.
323 for (uint32_t j = 0; j < verifLons; ++j) {
324 float sinTheta = sinThetaCache[j];
325 float cosTheta = cosThetaCache[j];
326
327 // North coordinate.
328 vertices[vHemiOffsetNorth] =
329 QVector3D(-yOffsetNorth, rhoCosPhiNorth * cosTheta, -rhoCosPhiNorth * sinTheta);
330
331 // North normal.
332 vertexNormals[vHemiOffsetNorth] =
333 QVector3D(sinPhiNorth, cosPhiNorth * cosTheta, -cosPhiNorth * sinTheta);
334
335 // South coordinate.
336 vertices[vHemiOffsetSouth] =
337 QVector3D(-yOffsetSouth, rhoCosPhiSouth * cosTheta, -rhoCosPhiSouth * sinTheta);
338
339 // South normal.
340 vertexNormals[vnHemiOffsetSouth] =
341 QVector3D(sinPhiSouth, cosPhiSouth * cosTheta, -cosPhiSouth * sinTheta);
342
343 ++vHemiOffsetNorth;
344 ++vHemiOffsetSouth;
345 ++vnHemiOffsetSouth;
346
347 uint32_t jNextVt = j + 1;
348 uint32_t jNextV = jNextVt % verifLons;
349
350 // North coordinate indices.
351 uint32_t vn00 = vCurrLatN + j;
352 uint32_t vn01 = vNextLatN + j;
353 uint32_t vn11 = vNextLatN + jNextV;
354 uint32_t vn10 = vCurrLatN + jNextV;
355
356 // South coordinate indices.
357 uint32_t vs00 = vCurrLatS + j;
358 uint32_t vs01 = vNextLatS + j;
359 uint32_t vs11 = vNextLatS + jNextV;
360 uint32_t vs10 = vCurrLatS + jNextV;
361
362 // North texture coordinate indices.
363 uint32_t vtn00 = vtCurrLatN + j;
364 uint32_t vtn01 = vtNextLatN + j;
365 uint32_t vtn11 = vtNextLatN + jNextVt;
366 uint32_t vtn10 = vtCurrLatN + jNextVt;
367
368 // South texture coordinate indices.
369 uint32_t vts00 = vtCurrLatS + j;
370 uint32_t vts01 = vtNextLatS + j;
371 uint32_t vts11 = vtNextLatS + jNextVt;
372 uint32_t vts10 = vtCurrLatS + jNextVt;
373
374 // North normal indices.
375 uint32_t vnn00 = vnCurrLatN + j;
376 uint32_t vnn01 = vnNextLatN + j;
377 uint32_t vnn11 = vnNextLatN + jNextV;
378 uint32_t vnn10 = vnCurrLatN + jNextV;
379
380 // South normal indices.
381 uint32_t vns00 = vnCurrLatS + j;
382 uint32_t vns01 = vnNextLatS + j;
383 uint32_t vns11 = vnNextLatS + jNextV;
384 uint32_t vns10 = vnCurrLatS + jNextV;
385
386 // North triangles.
387 faces[fHemiOffsetNorth] = { Face { .vertexIdx: vn00, .textureIdx: vtn00, .normalIdx: vnn00 }, Face { .vertexIdx: vn11, .textureIdx: vtn11, .normalIdx: vnn11 },
388 Face { .vertexIdx: vn10, .textureIdx: vtn10, .normalIdx: vnn10 } };
389
390 faces[fHemiOffsetNorth + 1] = { Face { .vertexIdx: vn00, .textureIdx: vtn00, .normalIdx: vnn00 },
391 Face { .vertexIdx: vn01, .textureIdx: vtn01, .normalIdx: vnn01 },
392 Face { .vertexIdx: vn11, .textureIdx: vtn11, .normalIdx: vnn11 } };
393
394 // South triangles.
395 faces[fHemiOffsetSouth] = { Face { .vertexIdx: vs00, .textureIdx: vts00, .normalIdx: vns00 }, Face { .vertexIdx: vs11, .textureIdx: vts11, .normalIdx: vns11 },
396 Face { .vertexIdx: vs10, .textureIdx: vts10, .normalIdx: vns10 } };
397
398 faces[fHemiOffsetSouth + 1] = { Face { .vertexIdx: vs00, .textureIdx: vts00, .normalIdx: vns00 },
399 Face { .vertexIdx: vs01, .textureIdx: vts01, .normalIdx: vns01 },
400 Face { .vertexIdx: vs11, .textureIdx: vts11, .normalIdx: vns11 } };
401
402 fHemiOffsetNorth += 2;
403 fHemiOffsetSouth += 2;
404 }
405
406 // For UVs, linear interpolation from North pole to
407 // North aspect ratio; and from South pole to South
408 // aspect ratio.
409 float tTexFac = ip1f * toTexVertical;
410 float tTexNorth = 1.f - tTexFac + tTexFac * vtAspectNorth;
411 float tTexSouth = vtAspectSouth * (1.f - tTexFac);
412
413 // Texture coordinates.
414 for (uint32_t j = 0; j < verifLonsp1; ++j) {
415 float sTex = sTexCache[j];
416
417 vertexTextures[vtHemiOffsetNorth] = QVector2D(sTex, tTexNorth);
418 vertexTextures[vtHemiOffsetSouth] = QVector2D(sTex, tTexSouth);
419
420 ++vtHemiOffsetNorth;
421 ++vtHemiOffsetSouth;
422 }
423 }
424
425 // Calculate sections of cylinder in middle.
426 if (calcMiddle) {
427
428 // Linear interpolation must exclude the origin (North equator)
429 // and the destination (South equator), so step must never equal
430 // 0.0 or 1.0 .
431 float toFac = 1.f / verifRingsp1;
432 uint32_t vCylOffset = idxVCyl;
433 uint32_t vtCylOffset = idxVtCyl;
434 for (uint32_t m = 1; m < verifRingsp1; ++m) {
435 float fac = m * toFac;
436 float cmplFac = 1.f - fac;
437
438 // Coordinates.
439 for (uint32_t j = 0; j < verifLons; ++j) {
440 QVector3D vEquatorNorth = vertices[idxVNEquator + j];
441 QVector3D vEquatorSouth = vertices[idxVSEquator + j];
442
443 // xy should be the same for both North and South.
444 // North z should equal half_depth while South z
445 // should equal -half_depth. However this is kept as
446 // a linear interpolation for clarity.
447 vertices[vCylOffset] =
448 QVector3D(cmplFac * vEquatorNorth.x() + fac * vEquatorSouth.x(),
449 cmplFac * vEquatorNorth.y() + fac * vEquatorSouth.y(),
450 cmplFac * vEquatorNorth.z() + fac * vEquatorSouth.z());
451
452 ++vCylOffset;
453 }
454
455 // Texture coordinates.
456 float tTex = cmplFac * vtAspectNorth + fac * vtAspectSouth;
457 for (uint32_t j = 0; j < verifLonsp1; ++j) {
458 float sTex = sTexCache[j];
459 vertexTextures[vtCylOffset] = QVector2D(sTex, tTex);
460 ++vtCylOffset;
461 }
462 }
463 }
464
465 // Cylinder face indices.
466 uint32_t fCylOffset = idxFsCyl;
467 for (uint32_t m = 0; m < verifRingsp1; ++m) {
468 uint32_t vCurrRing = idxVNEquator + m * verifLons;
469 uint32_t vNextRing = vCurrRing + verifLons;
470
471 uint32_t vtCurrRing = idxVtNEquator + m * verifLonsp1;
472 uint32_t vtNextRing = vtCurrRing + verifLonsp1;
473
474 for (uint32_t j = 0; j < verifLons; ++j) {
475 uint32_t jNextVt = j + 1;
476 uint32_t jNextV = jNextVt % verifLons;
477
478 // Coordinate corners.
479 uint32_t v00 = vCurrRing + j;
480 uint32_t v01 = vNextRing + j;
481 uint32_t v11 = vNextRing + jNextV;
482 uint32_t v10 = vCurrRing + jNextV;
483
484 // Texture coordinate corners.
485 uint32_t vt00 = vtCurrRing + j;
486 uint32_t vt01 = vtNextRing + j;
487 uint32_t vt11 = vtNextRing + jNextVt;
488 uint32_t vt10 = vtCurrRing + jNextVt;
489
490 // Normal corners.
491 uint32_t vn0 = idxVNEquator + j;
492 uint32_t vn1 = idxVNEquator + jNextV;
493
494 faces[fCylOffset] = { Face { .vertexIdx: v00, .textureIdx: vt00, .normalIdx: vn0 }, Face { .vertexIdx: v11, .textureIdx: vt11, .normalIdx: vn1 },
495 Face { .vertexIdx: v10, .textureIdx: vt10, .normalIdx: vn1 } };
496
497 faces[fCylOffset + 1] = { Face { .vertexIdx: v00, .textureIdx: vt00, .normalIdx: vn0 }, Face { .vertexIdx: v01, .textureIdx: vt01, .normalIdx: vn0 },
498 Face { .vertexIdx: v11, .textureIdx: vt11, .normalIdx: vn1 } };
499
500 fCylOffset += 2;
501 }
502 }
503
504 uint32_t stride = 3 * sizeof(float);
505 uint32_t strideNormal = 0;
506 uint32_t strideUV = 0;
507
508 if (m_enableNormals) {
509 strideNormal = stride;
510 stride += 3 * sizeof(float);
511 }
512 if (m_enableUV) {
513 strideUV = stride;
514 stride += 2 * sizeof(float);
515 }
516
517 QByteArray vertexData(vertices.length() * stride, Qt::Initialization::Uninitialized);
518 QByteArray indexData(faces.length() * 3 * sizeof(quint32), Qt::Initialization::Uninitialized);
519
520 const auto getVertexPtr = [&](const int vertexIdx) {
521 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx);
522 };
523 const auto getNormalPtr = [&](const int vertexIdx) {
524 return reinterpret_cast<QVector3D *>(vertexData.data() + stride * vertexIdx + strideNormal);
525 };
526 const auto getTexturePtr = [&](const int vertexIdx) {
527 return reinterpret_cast<QVector2D *>(vertexData.data() + stride * vertexIdx + strideUV);
528 };
529
530 uint32_t *indexPtr = reinterpret_cast<uint32_t *>(indexData.data());
531
532 for (qsizetype i = 0; i < vertices.length(); i++) {
533 *getVertexPtr(i) = vertices[i];
534 }
535
536 for (qsizetype i = 0; i < faces.length(); i++) {
537 const auto vertexIndices =
538 std::array<uint32_t, 3> { faces[i][0].vertexIdx, faces[i][1].vertexIdx,
539 faces[i][2].vertexIdx };
540 *indexPtr = vertexIndices[0];
541 indexPtr++;
542 *indexPtr = vertexIndices[1];
543 indexPtr++;
544 *indexPtr = vertexIndices[2];
545 indexPtr++;
546
547 if (m_enableNormals) {
548 const auto normalIndices =
549 std::array<uint32_t, 3> { faces[i][0].normalIdx, faces[i][1].normalIdx,
550 faces[i][2].normalIdx };
551 *getNormalPtr(vertexIndices[0]) = vertexNormals[normalIndices[0]];
552 *getNormalPtr(vertexIndices[1]) = vertexNormals[normalIndices[1]];
553 *getNormalPtr(vertexIndices[2]) = vertexNormals[normalIndices[2]];
554 }
555
556 if (m_enableUV) {
557 const auto textureIndices =
558 std::array<uint32_t, 3> { faces[i][0].textureIdx, faces[i][1].textureIdx,
559 faces[i][2].textureIdx };
560 *getTexturePtr(vertexIndices[0]) = vertexTextures[textureIndices[0]];
561 *getTexturePtr(vertexIndices[1]) = vertexTextures[textureIndices[1]];
562 *getTexturePtr(vertexIndices[2]) = vertexTextures[textureIndices[2]];
563 }
564 }
565
566 addAttribute(semantic: QQuick3DGeometry::Attribute::PositionSemantic, offset: 0,
567 componentType: QQuick3DGeometry::Attribute::ComponentType::F32Type);
568 if (m_enableNormals) {
569 addAttribute(semantic: QQuick3DGeometry::Attribute::NormalSemantic, offset: strideNormal,
570 componentType: QQuick3DGeometry::Attribute::ComponentType::F32Type);
571 }
572 if (m_enableUV) {
573 addAttribute(semantic: QQuick3DGeometry::Attribute::TexCoordSemantic, offset: strideUV,
574 componentType: QQuick3DGeometry::Attribute::ComponentType::F32Type);
575 }
576 addAttribute(semantic: QQuick3DGeometry::Attribute::IndexSemantic, offset: 0,
577 componentType: QQuick3DGeometry::Attribute::ComponentType::U32Type);
578
579 setStride(stride);
580 setVertexData(vertexData);
581 setIndexData(indexData);
582
583 setBounds(min: QVector3D(-radius - 0.5f * m_height, -radius, -radius),
584 max: QVector3D(radius + 0.5f * m_height, radius, radius));
585}
586
587QT_END_NAMESPACE
588

source code of qtquick3dphysics/src/helpers/qcapsulegeometry.cpp