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