1 | // Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qtorusgeometry.h" |
5 | #include "qtorusgeometry_p.h" |
6 | |
7 | #include <Qt3DCore/qbuffer.h> |
8 | #include <Qt3DCore/qattribute.h> |
9 | #include <QtGui/QVector3D> |
10 | #include <QtGui/QVector4D> |
11 | |
12 | #include <qmath.h> |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | |
17 | namespace Qt3DExtras { |
18 | |
19 | using namespace Qt3DCore; |
20 | |
21 | namespace { |
22 | |
23 | int vertexCount(int requestedRings, int requestedSlices) |
24 | { |
25 | return (requestedRings + 1) * (requestedSlices + 1); |
26 | } |
27 | |
28 | int triangleCount(int requestedRings, int requestedSlices) |
29 | { |
30 | return 2 * requestedRings * requestedSlices; |
31 | } |
32 | |
33 | QByteArray createTorusVertexData(double radius, double minorRadius, |
34 | int rings, int slices) |
35 | { |
36 | // The additional side and ring compared to what the user asked |
37 | // for is because we also need to ensure proper texture coordinate |
38 | // wrapping at the seams. Without this the last ring and side would |
39 | // interpolate the texture coordinates from ~0.9x back down to 0 |
40 | // (the starting value at the first ring). So we insert an extra |
41 | // ring and side with the same positions as the first ring and side |
42 | // but with texture coordinates of 1 (compared to 0). |
43 | const int nVerts = vertexCount(requestedRings: rings, requestedSlices: slices); |
44 | QByteArray bufferBytes; |
45 | // vec3 pos, vec2 texCoord, vec3 normal, vec4 tangent |
46 | const quint32 elementSize = 3 + 2 + 3 + 4; |
47 | const quint32 stride = elementSize * sizeof(float); |
48 | bufferBytes.resize(size: stride * nVerts); |
49 | |
50 | float* fptr = reinterpret_cast<float*>(bufferBytes.data()); |
51 | |
52 | const float ringFactor = (M_PI * 2) / static_cast<float>(rings); |
53 | const float sliceFactor = (M_PI * 2) / static_cast<float>(slices); |
54 | |
55 | for (int ring = 0; ring <= rings; ++ring) { |
56 | const float u = ring * ringFactor; |
57 | const float cu = qCos(v: u); |
58 | const float su = qSin(v: u); |
59 | |
60 | for (int slice = 0; slice <= slices; ++slice) { |
61 | const float v = slice * sliceFactor; |
62 | const float cv = qCos(v: v + M_PI); |
63 | const float sv = qSin(v); |
64 | const float r = (radius + minorRadius * cv); |
65 | |
66 | *fptr++ = r * cu; |
67 | *fptr++ = r * su; |
68 | *fptr++ = minorRadius * sv; |
69 | |
70 | *fptr++ = u / (M_PI * 2); |
71 | *fptr++ = v / (M_PI * 2); |
72 | |
73 | QVector3D n(cv * cu, cv * su, sv); |
74 | n.normalize(); |
75 | *fptr++ = n.x(); |
76 | *fptr++ = n.y(); |
77 | *fptr++ = n.z(); |
78 | |
79 | QVector4D t(-su, cu, 0.0f, 1.0f); |
80 | t.normalize(); |
81 | *fptr++ = t.x(); |
82 | *fptr++ = t.y(); |
83 | *fptr++ = t.z(); |
84 | *fptr++ = t.w(); |
85 | } |
86 | } |
87 | |
88 | return bufferBytes; |
89 | } |
90 | |
91 | QByteArray createTorusIndexData(int requestedRings, int requestedSlices) |
92 | { |
93 | const int slices = requestedSlices + 1; |
94 | int triangles = triangleCount(requestedRings, requestedSlices); |
95 | qsizetype indices = triangles * 3; |
96 | Q_ASSERT(indices < 65536); |
97 | QByteArray indexBytes; |
98 | indexBytes.resize(size: indices * sizeof(quint16)); |
99 | quint16* indexPtr = reinterpret_cast<quint16*>(indexBytes.data()); |
100 | |
101 | for (int ring = 0; ring < requestedRings; ++ring) { |
102 | const int ringStart = ring * slices; |
103 | const int nextRingStart = (ring + 1) * slices; |
104 | for (int slice = 0; slice < requestedSlices; ++slice) { |
105 | const int nextSlice = (slice + 1) % slices; |
106 | *indexPtr++ = ringStart + slice; |
107 | *indexPtr++ = ringStart + nextSlice; |
108 | *indexPtr++ = nextRingStart + slice; |
109 | *indexPtr++ = ringStart + nextSlice; |
110 | *indexPtr++ = nextRingStart + nextSlice; |
111 | *indexPtr++ = nextRingStart + slice; |
112 | } |
113 | } |
114 | |
115 | return indexBytes; |
116 | } |
117 | |
118 | } // anonymous |
119 | |
120 | QTorusGeometryPrivate::QTorusGeometryPrivate() |
121 | : QGeometryPrivate() |
122 | , m_rings(16) |
123 | , m_slices(16) |
124 | , m_radius(1.0f) |
125 | , m_minorRadius(1.0f) |
126 | , m_positionAttribute(nullptr) |
127 | , m_normalAttribute(nullptr) |
128 | , m_texCoordAttribute(nullptr) |
129 | , m_tangentAttribute(nullptr) |
130 | , m_indexAttribute(nullptr) |
131 | , m_vertexBuffer(nullptr) |
132 | , m_indexBuffer(nullptr) |
133 | { |
134 | } |
135 | |
136 | void QTorusGeometryPrivate::init() |
137 | { |
138 | Q_Q(QTorusGeometry); |
139 | m_positionAttribute = new QAttribute(q); |
140 | m_normalAttribute = new QAttribute(q); |
141 | m_texCoordAttribute = new QAttribute(q); |
142 | m_tangentAttribute = new QAttribute(q); |
143 | m_indexAttribute = new QAttribute(q); |
144 | m_vertexBuffer = new Qt3DCore::QBuffer(q); |
145 | m_indexBuffer = new Qt3DCore::QBuffer(q); |
146 | // vec3 pos, vec2 tex, vec3 normal, vec4 tangent |
147 | const quint32 elementSize = 3 + 2 + 3 + 4; |
148 | const quint32 stride = elementSize * sizeof(float); |
149 | const int nVerts = vertexCount(requestedRings: m_rings, requestedSlices: m_slices); |
150 | const int triangles = triangleCount(requestedRings: m_rings, requestedSlices: m_slices); |
151 | |
152 | m_positionAttribute->setName(QAttribute::defaultPositionAttributeName()); |
153 | m_positionAttribute->setVertexBaseType(QAttribute::Float); |
154 | m_positionAttribute->setVertexSize(3); |
155 | m_positionAttribute->setAttributeType(QAttribute::VertexAttribute); |
156 | m_positionAttribute->setBuffer(m_vertexBuffer); |
157 | m_positionAttribute->setByteStride(stride); |
158 | m_positionAttribute->setCount(nVerts); |
159 | |
160 | m_texCoordAttribute->setName(QAttribute::defaultTextureCoordinateAttributeName()); |
161 | m_texCoordAttribute->setVertexBaseType(QAttribute::Float); |
162 | m_texCoordAttribute->setVertexSize(2); |
163 | m_texCoordAttribute->setAttributeType(QAttribute::VertexAttribute); |
164 | m_texCoordAttribute->setBuffer(m_vertexBuffer); |
165 | m_texCoordAttribute->setByteStride(stride); |
166 | m_texCoordAttribute->setByteOffset(3 * sizeof(float)); |
167 | m_texCoordAttribute->setCount(nVerts); |
168 | |
169 | m_normalAttribute->setName(QAttribute::defaultNormalAttributeName()); |
170 | m_normalAttribute->setVertexBaseType(QAttribute::Float); |
171 | m_normalAttribute->setVertexSize(3); |
172 | m_normalAttribute->setAttributeType(QAttribute::VertexAttribute); |
173 | m_normalAttribute->setBuffer(m_vertexBuffer); |
174 | m_normalAttribute->setByteStride(stride); |
175 | m_normalAttribute->setByteOffset(5 * sizeof(float)); |
176 | m_normalAttribute->setCount(nVerts); |
177 | |
178 | m_tangentAttribute->setName(QAttribute::defaultTangentAttributeName()); |
179 | m_tangentAttribute->setVertexBaseType(QAttribute::Float); |
180 | m_tangentAttribute->setVertexSize(4); |
181 | m_tangentAttribute->setAttributeType(QAttribute::VertexAttribute); |
182 | m_tangentAttribute->setBuffer(m_vertexBuffer); |
183 | m_tangentAttribute->setByteStride(stride); |
184 | m_tangentAttribute->setByteOffset(8 * sizeof(float)); |
185 | m_tangentAttribute->setCount(nVerts); |
186 | |
187 | m_indexAttribute->setAttributeType(QAttribute::IndexAttribute); |
188 | m_indexAttribute->setVertexBaseType(QAttribute::UnsignedShort); |
189 | m_indexAttribute->setBuffer(m_indexBuffer); |
190 | |
191 | m_indexAttribute->setCount(triangles * 3); |
192 | |
193 | m_vertexBuffer->setData(generateVertexData()); |
194 | m_indexBuffer->setData(generateIndexData()); |
195 | |
196 | q->addAttribute(attribute: m_positionAttribute); |
197 | q->addAttribute(attribute: m_texCoordAttribute); |
198 | q->addAttribute(attribute: m_normalAttribute); |
199 | q->addAttribute(attribute: m_tangentAttribute); |
200 | q->addAttribute(attribute: m_indexAttribute); |
201 | } |
202 | |
203 | QByteArray QTorusGeometryPrivate::generateVertexData() const |
204 | { |
205 | return createTorusVertexData(radius: m_radius, minorRadius: m_minorRadius, rings: m_rings, slices: m_slices); |
206 | } |
207 | |
208 | QByteArray QTorusGeometryPrivate::generateIndexData() const |
209 | { |
210 | return createTorusIndexData(requestedRings: m_rings, requestedSlices: m_slices); |
211 | } |
212 | |
213 | /*! |
214 | * \qmltype TorusGeometry |
215 | * \nativetype Qt3DExtras::QTorusGeometry |
216 | * \inqmlmodule Qt3D.Extras |
217 | * \brief TorusGeometry allows creation of a torus in 3D space. |
218 | * |
219 | * The TorusGeometry type is most commonly used internally by the TorusMesh type |
220 | * but can also be used in custom GeometryRenderer types. |
221 | */ |
222 | |
223 | /*! |
224 | * \qmlproperty int TorusGeometry::rings |
225 | * |
226 | * Holds the number of rings in the torus. |
227 | */ |
228 | |
229 | /*! |
230 | * \qmlproperty int TorusGeometry::slices |
231 | * |
232 | * Holds the number of slices in the torus. |
233 | */ |
234 | |
235 | /*! |
236 | * \qmlproperty real TorusGeometry::radius |
237 | * |
238 | * Holds the outer radius of the torus. |
239 | */ |
240 | |
241 | /*! |
242 | * \qmlproperty real TorusGeometry::minorRadius |
243 | * |
244 | * Holds the inner radius of the torus. |
245 | */ |
246 | |
247 | /*! |
248 | * \qmlproperty Attribute TorusGeometry::positionAttribute |
249 | * |
250 | * Holds the geometry position attribute. |
251 | */ |
252 | |
253 | /*! |
254 | * \qmlproperty Attribute TorusGeometry::normalAttribute |
255 | * |
256 | * Holds the geometry normal attribute. |
257 | */ |
258 | |
259 | /*! |
260 | * \qmlproperty Attribute TorusGeometry::texCoordAttribute |
261 | * |
262 | * Holds the geometry texture coordinate attribute. |
263 | */ |
264 | |
265 | /*! |
266 | * \qmlproperty Attribute TorusGeometry::indexAttribute |
267 | * |
268 | * Holds the geometry index attribute. |
269 | */ |
270 | |
271 | /*! |
272 | * \class Qt3DExtras::QTorusGeometry |
273 | * \ingroup qt3d-extras-geometries |
274 | * \inheaderfile Qt3DExtras/QTorusGeometry |
275 | * \inmodule Qt3DExtras |
276 | * \brief The QTorusGeometry class allows creation of a torus in 3D space. |
277 | * \since 5.7 |
278 | * \ingroup geometries |
279 | * \inherits Qt3DCore::QGeometry |
280 | * |
281 | * The QTorusGeometry class is most commonly used internally by the QTorusMesh |
282 | * but can also be used in custom Qt3DRender::QGeometryRenderer subclasses. |
283 | */ |
284 | |
285 | /*! |
286 | * Constructs a new QTorusGeometry with \a parent. |
287 | */ |
288 | QTorusGeometry::QTorusGeometry(QNode *parent) |
289 | : QGeometry(*new QTorusGeometryPrivate(), parent) |
290 | { |
291 | Q_D(QTorusGeometry); |
292 | d->init(); |
293 | } |
294 | |
295 | /*! |
296 | * \internal |
297 | */ |
298 | QTorusGeometry::QTorusGeometry(QTorusGeometryPrivate &dd, QNode *parent) |
299 | : QGeometry(dd, parent) |
300 | { |
301 | Q_D(QTorusGeometry); |
302 | d->init(); |
303 | } |
304 | |
305 | /*! |
306 | * \internal |
307 | */ |
308 | QTorusGeometry::~QTorusGeometry() |
309 | { |
310 | } |
311 | |
312 | /*! |
313 | * Updates vertices based on rings, slices, and radius properties. |
314 | */ |
315 | void QTorusGeometry::updateVertices() |
316 | { |
317 | Q_D(QTorusGeometry); |
318 | const int nVerts = vertexCount(requestedRings: d->m_rings, requestedSlices: d->m_slices); |
319 | d->m_positionAttribute->setCount(nVerts); |
320 | d->m_texCoordAttribute->setCount(nVerts); |
321 | d->m_normalAttribute->setCount(nVerts); |
322 | d->m_vertexBuffer->setData(d->generateVertexData()); |
323 | } |
324 | |
325 | /*! |
326 | * Updates indices based on rings and slices properties. |
327 | */ |
328 | void QTorusGeometry::updateIndices() |
329 | { |
330 | Q_D(QTorusGeometry); |
331 | const int triangles = triangleCount(requestedRings: d->m_rings, requestedSlices: d->m_slices); |
332 | d->m_indexAttribute->setCount(triangles * 3); |
333 | d->m_indexBuffer->setData(d->generateIndexData()); |
334 | } |
335 | |
336 | void QTorusGeometry::setRings(int rings) |
337 | { |
338 | Q_D(QTorusGeometry); |
339 | if (rings != d->m_rings) { |
340 | d->m_rings = rings; |
341 | updateVertices(); |
342 | updateIndices(); |
343 | emit ringsChanged(rings); |
344 | } |
345 | } |
346 | |
347 | void QTorusGeometry::setSlices(int slices) |
348 | { |
349 | Q_D(QTorusGeometry); |
350 | if (slices != d->m_slices) { |
351 | d->m_slices = slices; |
352 | updateVertices(); |
353 | updateIndices(); |
354 | emit slicesChanged(slices); |
355 | } |
356 | } |
357 | |
358 | void QTorusGeometry::setRadius(float radius) |
359 | { |
360 | Q_D(QTorusGeometry); |
361 | if (radius != d->m_radius) { |
362 | d->m_radius = radius; |
363 | updateVertices(); |
364 | emit radiusChanged(radius); |
365 | } |
366 | } |
367 | |
368 | void QTorusGeometry::setMinorRadius(float minorRadius) |
369 | { |
370 | Q_D(QTorusGeometry); |
371 | if (minorRadius != d->m_minorRadius) { |
372 | d->m_minorRadius = minorRadius; |
373 | updateVertices(); |
374 | emit minorRadiusChanged(minorRadius); |
375 | } |
376 | } |
377 | |
378 | /*! |
379 | * \property Qt3DExtras::QTorusGeometry::rings |
380 | * |
381 | * Holds the number of rings in the torus. |
382 | */ |
383 | int QTorusGeometry::rings() const |
384 | { |
385 | Q_D(const QTorusGeometry); |
386 | return d->m_rings; |
387 | } |
388 | |
389 | /*! |
390 | * \property Qt3DExtras::QTorusGeometry::slices |
391 | * |
392 | * Holds the number of slices in the torus. |
393 | */ |
394 | int QTorusGeometry::slices() const |
395 | { |
396 | Q_D(const QTorusGeometry); |
397 | return d->m_slices; |
398 | } |
399 | |
400 | /*! |
401 | * \property Qt3DExtras::QTorusGeometry::radius |
402 | * |
403 | * Holds the outer radius of the torus. |
404 | */ |
405 | float QTorusGeometry::radius() const |
406 | { |
407 | Q_D(const QTorusGeometry); |
408 | return d->m_radius; |
409 | } |
410 | |
411 | /*! |
412 | * \property Qt3DExtras::QTorusGeometry::minorRadius |
413 | * |
414 | * Holds the inner radius of the torus. |
415 | */ |
416 | float QTorusGeometry::minorRadius() const |
417 | { |
418 | Q_D(const QTorusGeometry); |
419 | return d->m_minorRadius; |
420 | } |
421 | |
422 | /*! |
423 | * \property Qt3DExtras::QTorusGeometry::positionAttribute |
424 | * |
425 | * Holds the geometry position attribute. |
426 | */ |
427 | QAttribute *QTorusGeometry::positionAttribute() const |
428 | { |
429 | Q_D(const QTorusGeometry); |
430 | return d->m_positionAttribute; |
431 | } |
432 | |
433 | /*! |
434 | * \property Qt3DExtras::QTorusGeometry::normalAttribute |
435 | * |
436 | * Holds the geometry normal attribute. |
437 | */ |
438 | QAttribute *QTorusGeometry::normalAttribute() const |
439 | { |
440 | Q_D(const QTorusGeometry); |
441 | return d->m_normalAttribute; |
442 | } |
443 | |
444 | /*! |
445 | * \property Qt3DExtras::QTorusGeometry::texCoordAttribute |
446 | * |
447 | * Holds the geometry texture coordinate attribute. |
448 | */ |
449 | QAttribute *QTorusGeometry::texCoordAttribute() const |
450 | { |
451 | Q_D(const QTorusGeometry); |
452 | return d->m_texCoordAttribute; |
453 | } |
454 | |
455 | /*! |
456 | * \property Qt3DExtras::QTorusGeometry::indexAttribute |
457 | * |
458 | * Holds the geometry index attribute. |
459 | */ |
460 | QAttribute *QTorusGeometry::indexAttribute() const |
461 | { |
462 | Q_D(const QTorusGeometry); |
463 | return d->m_indexAttribute; |
464 | } |
465 | |
466 | } // Qt3DExtras |
467 | |
468 | QT_END_NAMESPACE |
469 | |
470 | #include "moc_qtorusgeometry.cpp" |
471 |
Definitions
- vertexCount
- triangleCount
- createTorusVertexData
- createTorusIndexData
- QTorusGeometryPrivate
- init
- generateVertexData
- generateIndexData
- QTorusGeometry
- QTorusGeometry
- ~QTorusGeometry
- updateVertices
- updateIndices
- setRings
- setSlices
- setRadius
- setMinorRadius
- rings
- slices
- radius
- minorRadius
- positionAttribute
- normalAttribute
- texCoordAttribute
Learn Advanced QML with KDAB
Find out more