Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcapsulegeometry.cpp
Go to the documentation of this file.
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
8
9#include <QVector3D>
10
12
14{
15 updateData();
16}
17
19{
20 if (m_enableNormals == enable)
21 return;
22
23 m_enableNormals = enable;
25 updateData();
26 update();
27}
28
30{
31 if (m_enableUV == enable)
32 return;
33
34 m_enableUV = enable;
36 updateData();
37 update();
38}
39
41{
42 if (m_longitudes == longitudes)
43 return;
44
45 m_longitudes = longitudes;
47 updateData();
48 update();
49}
50
52{
53 if (m_latitudes == latitudes)
54 return;
55
56 m_latitudes = latitudes;
58 updateData();
59 update();
60}
61
63{
64 if (m_rings == rings)
65 return;
66
67 m_rings = rings;
69 updateData();
70 update();
71}
72
74{
75 if (m_height == height)
76 return;
77
78 m_height = height;
80 updateData();
81 update();
82}
83
84void CapsuleGeometry::setDiameter(float diameter)
85{
86 if (m_diameter == diameter)
87 return;
88
89 m_diameter = diameter;
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(2, m_latitudes);
114 if (verifLats % 2 != 0) {
115 verifLats += 1;
116 }
117
118 // Validate input arguments.
119 uint32_t verifLons = qMax(3, m_longitudes);
120 uint32_t verifRings = qMax(0, m_rings);
121 float verifDepth = qMax(EPSILON, m_height);
122 float verifRad = qMax(EPSILON, 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(theta);
204 float cosTheta = cos(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 { 0, j, 0 }, Face { jNextVt, verifLons + j, jNextVt },
230 Face { 1 + jNextV, verifLons + jNextVt, 1 + jNextV } };
231
232 // South triangle.
233 faces[idxFsSouthHemi + j] = {
234 Face { idxVSouthPole, idxVtSCap + j, idxVnSouthPole },
235 Face { idxVSouthCap + jNextV, idxVtSPolar + jNextVt, idxVnSouthCap + jNextV },
236 Face { idxVSouthCap + j, idxVtSPolar + j, 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(phi);
281 float cosPhiSouth = cos(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 { vn00, vtn00, vnn00 }, Face { vn11, vtn11, vnn11 },
388 Face { vn10, vtn10, vnn10 } };
389
390 faces[fHemiOffsetNorth + 1] = { Face { vn00, vtn00, vnn00 },
391 Face { vn01, vtn01, vnn01 },
392 Face { vn11, vtn11, vnn11 } };
393
394 // South triangles.
395 faces[fHemiOffsetSouth] = { Face { vs00, vts00, vns00 }, Face { vs11, vts11, vns11 },
396 Face { vs10, vts10, vns10 } };
397
398 faces[fHemiOffsetSouth + 1] = { Face { vs00, vts00, vns00 },
399 Face { vs01, vts01, vns01 },
400 Face { vs11, vts11, 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 { v00, vt00, vn0 }, Face { v11, vt11, vn1 },
495 Face { v10, vt10, vn1 } };
496
497 faces[fCylOffset + 1] = { Face { v00, vt00, vn0 }, Face { v01, vt01, vn0 },
498 Face { v11, vt11, 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
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
568 if (m_enableNormals) {
571 }
572 if (m_enableUV) {
575 }
578
582
583 setBounds(QVector3D(-radius - 0.5f * m_height, -radius, -radius),
584 QVector3D(radius + 0.5f * m_height, radius, radius));
585}
586
void setLongitudes(int longitudes)
void setEnableNormals(bool enable)
void latitudesChanged()
void longitudesChanged()
void setHeight(float height)
void setRings(int rings)
void diameterChanged()
void enableUVChanged()
void setLatitudes(int latitudes)
void setEnableUV(bool enable)
void enableNormalsChanged()
void heightChanged()
void setDiameter(float diameter)
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
Attribute::Semantic int int stride
Returns the byte stride of the vertex buffer.
void setStride(int stride)
Sets the stride of the vertex buffer to stride, measured in bytes.
void addAttribute(Attribute::Semantic semantic, int offset, Attribute::ComponentType componentType)
Adds vertex attribute description.
void setVertexData(const QByteArray &data)
Sets the vertex buffer data.
void clear()
Resets the geometry to its initial state, clearing previously set vertex and index data as well as at...
void setBounds(const QVector3D &min, const QVector3D &max)
Sets the bounding volume of the geometry to the cube defined by the points min and max.
QByteArray vertexData() const
Returns the vertex buffer data set by setVertexData.
QByteArray indexData() const
Returns the index buffer data.
void setIndexData(const QByteArray &data)
Sets the index buffer to data.
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
Combined button and popup list for selecting options.
#define M_PI
Definition qmath.h:209
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLint GLsizei GLsizei height
const void GLsizei GLsizei stride
GLboolean enable
#define emit
unsigned int quint32
Definition qtypes.h:50
ptrdiff_t qsizetype
Definition qtypes.h:165
uint32_t normalIdx
uint32_t vertexIdx
uint32_t textureIdx