1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qspheregeometry.h" |
41 | #include "qspheregeometry_p.h" |
42 | |
43 | #include <Qt3DRender/qbufferdatagenerator.h> |
44 | #include <Qt3DRender/qbuffer.h> |
45 | #include <Qt3DRender/qattribute.h> |
46 | |
47 | #include <qmath.h> |
48 | |
49 | #ifndef _USE_MATH_DEFINES |
50 | # define _USE_MATH_DEFINES // For MSVC |
51 | #endif |
52 | |
53 | |
54 | QT_BEGIN_NAMESPACE |
55 | |
56 | using namespace Qt3DRender; |
57 | |
58 | namespace Qt3DExtras { |
59 | |
60 | namespace { |
61 | |
62 | QByteArray (float radius, int rings, int slices) |
63 | { |
64 | QByteArray bufferBytes; |
65 | // vec3 pos, vec2 texCoord, vec3 normal, vec4 tangent |
66 | const quint32 elementSize = 3 + 2 + 3 + 4; |
67 | const quint32 stride = elementSize * sizeof(float); |
68 | const int nVerts = (slices + 1) * (rings + 1); |
69 | bufferBytes.resize(size: stride * nVerts); |
70 | |
71 | float* fptr = reinterpret_cast<float*>(bufferBytes.data()); |
72 | |
73 | const float dTheta = (M_PI * 2) / static_cast<float>( slices ); |
74 | const float dPhi = M_PI / static_cast<float>( rings ); |
75 | const float du = 1.0f / static_cast<float>( slices ); |
76 | const float dv = 1.0f / static_cast<float>( rings ); |
77 | |
78 | // Iterate over latitudes (rings) |
79 | for ( int lat = 0; lat < rings + 1; ++lat ) |
80 | { |
81 | const float phi = M_PI_2 - static_cast<float>( lat ) * dPhi; |
82 | const float cosPhi = qCos( v: phi ); |
83 | const float sinPhi = qSin( v: phi ); |
84 | const float v = 1.0f - static_cast<float>( lat ) * dv; |
85 | |
86 | // Iterate over longitudes (slices) |
87 | for ( int lon = 0; lon < slices + 1; ++lon ) |
88 | { |
89 | const float theta = static_cast<float>( lon ) * dTheta; |
90 | const float cosTheta = qCos( v: theta ); |
91 | const float sinTheta = qSin( v: theta ); |
92 | const float u = static_cast<float>( lon ) * du; |
93 | |
94 | *fptr++ = radius * cosTheta * cosPhi; |
95 | *fptr++ = radius * sinPhi; |
96 | *fptr++ = radius * sinTheta * cosPhi; |
97 | |
98 | *fptr++ = u; |
99 | *fptr++ = v; |
100 | |
101 | *fptr++ = cosTheta * cosPhi; |
102 | *fptr++ = sinPhi; |
103 | *fptr++ = sinTheta * cosPhi; |
104 | |
105 | *fptr++ = sinTheta; |
106 | *fptr++ = 0.0; |
107 | *fptr++ = -cosTheta; |
108 | *fptr++ = 1.0; |
109 | } |
110 | } |
111 | return bufferBytes; |
112 | } |
113 | |
114 | QByteArray (int rings, int slices) |
115 | { |
116 | int faces = (slices * 2) * (rings - 2); // two tris per slice, for all middle rings |
117 | faces += 2 * slices; // tri per slice for both top and bottom |
118 | |
119 | QByteArray indexBytes; |
120 | const int indices = faces * 3; |
121 | Q_ASSERT(indices < 65536); |
122 | indexBytes.resize(size: indices * sizeof(quint16)); |
123 | quint16 *indexPtr = reinterpret_cast<quint16*>(indexBytes.data()); |
124 | |
125 | // top cap |
126 | { |
127 | const int nextRingStartIndex = slices + 1; |
128 | for ( int j = 0; j < slices; ++j ) |
129 | { |
130 | *indexPtr++ = nextRingStartIndex + j; |
131 | *indexPtr++ = 0; |
132 | *indexPtr++ = nextRingStartIndex + j + 1; |
133 | } |
134 | } |
135 | |
136 | for ( int i = 1; i < (rings - 1); ++i ) |
137 | { |
138 | const int ringStartIndex = i * ( slices + 1 ); |
139 | const int nextRingStartIndex = ( i + 1 ) * ( slices + 1 ); |
140 | |
141 | for ( int j = 0; j < slices; ++j ) |
142 | { |
143 | // Split the quad into two triangles |
144 | *indexPtr++ = ringStartIndex + j; |
145 | *indexPtr++ = ringStartIndex + j + 1; |
146 | *indexPtr++ = nextRingStartIndex + j; |
147 | *indexPtr++ = nextRingStartIndex + j; |
148 | *indexPtr++ = ringStartIndex + j + 1; |
149 | *indexPtr++ = nextRingStartIndex + j + 1; |
150 | } |
151 | } |
152 | |
153 | // bottom cap |
154 | { |
155 | const int ringStartIndex = (rings - 1) * ( slices + 1); |
156 | const int nextRingStartIndex = (rings) * ( slices + 1); |
157 | for ( int j = 0; j < slices; ++j ) |
158 | { |
159 | *indexPtr++ = ringStartIndex + j + 1; |
160 | *indexPtr++ = nextRingStartIndex; |
161 | *indexPtr++ = ringStartIndex + j; |
162 | } |
163 | } |
164 | |
165 | return indexBytes; |
166 | } |
167 | |
168 | } // anonymous |
169 | |
170 | class : public QBufferDataGenerator |
171 | { |
172 | public: |
173 | (int rings, int slices, float radius) |
174 | : m_rings(rings) |
175 | , m_slices(slices) |
176 | , m_radius(radius) |
177 | { |
178 | } |
179 | |
180 | QByteArray () override |
181 | { |
182 | return createSphereMeshVertexData(radius: m_radius, rings: m_rings, slices: m_slices); |
183 | } |
184 | |
185 | bool (const QBufferDataGenerator &other) const override |
186 | { |
187 | const SphereVertexDataFunctor *otherFunctor = functor_cast<SphereVertexDataFunctor>(other: &other); |
188 | if (otherFunctor != nullptr) |
189 | return (otherFunctor->m_rings == m_rings && |
190 | otherFunctor->m_slices == m_slices && |
191 | otherFunctor->m_radius == m_radius); |
192 | return false; |
193 | } |
194 | |
195 | QT3D_FUNCTOR(SphereVertexDataFunctor) |
196 | |
197 | private: |
198 | int ; |
199 | int ; |
200 | float ; |
201 | }; |
202 | |
203 | class : public QBufferDataGenerator |
204 | { |
205 | public: |
206 | (int rings, int slices) |
207 | : m_rings(rings) |
208 | , m_slices(slices) |
209 | { |
210 | } |
211 | |
212 | QByteArray () override |
213 | { |
214 | return createSphereMeshIndexData(rings: m_rings, slices: m_slices); |
215 | } |
216 | |
217 | bool (const QBufferDataGenerator &other) const override |
218 | { |
219 | const SphereIndexDataFunctor *otherFunctor = functor_cast<SphereIndexDataFunctor>(other: &other); |
220 | if (otherFunctor != nullptr) |
221 | return (otherFunctor->m_rings == m_rings && |
222 | otherFunctor->m_slices == m_slices); |
223 | return false; |
224 | } |
225 | |
226 | QT3D_FUNCTOR(SphereIndexDataFunctor) |
227 | |
228 | private: |
229 | int ; |
230 | int ; |
231 | }; |
232 | |
233 | QSphereGeometryPrivate::() |
234 | : QGeometryPrivate() |
235 | , m_generateTangents(false) |
236 | , m_rings(16) |
237 | , m_slices(16) |
238 | , m_radius(1.0f) |
239 | , m_positionAttribute(nullptr) |
240 | , m_normalAttribute(nullptr) |
241 | , m_texCoordAttribute(nullptr) |
242 | , m_tangentAttribute(nullptr) |
243 | , m_indexAttribute(nullptr) |
244 | , m_vertexBuffer(nullptr) |
245 | , m_indexBuffer(nullptr) |
246 | { |
247 | } |
248 | |
249 | void QSphereGeometryPrivate::() |
250 | { |
251 | Q_Q(QSphereGeometry); |
252 | m_positionAttribute = new QAttribute(q); |
253 | m_normalAttribute = new QAttribute(q); |
254 | m_texCoordAttribute = new QAttribute(q); |
255 | m_tangentAttribute = new QAttribute(q); |
256 | m_indexAttribute = new QAttribute(q); |
257 | m_vertexBuffer = new Qt3DRender::QBuffer(q); |
258 | m_indexBuffer = new Qt3DRender::QBuffer(q); |
259 | |
260 | // vec3 pos, vec2 tex, vec3 normal, vec4 tangent |
261 | const quint32 elementSize = 3 + 2 + 3 + 4; |
262 | const quint32 stride = elementSize * sizeof(float); |
263 | const int nVerts = (m_slices + 1) * (m_rings + 1); |
264 | const int faces = (m_slices * 2) * (m_rings - 2) + (2 * m_slices); |
265 | |
266 | m_positionAttribute->setName(QAttribute::defaultPositionAttributeName()); |
267 | m_positionAttribute->setVertexBaseType(QAttribute::Float); |
268 | m_positionAttribute->setVertexSize(3); |
269 | m_positionAttribute->setAttributeType(QAttribute::VertexAttribute); |
270 | m_positionAttribute->setBuffer(m_vertexBuffer); |
271 | m_positionAttribute->setByteStride(stride); |
272 | m_positionAttribute->setCount(nVerts); |
273 | |
274 | m_texCoordAttribute->setName(QAttribute::defaultTextureCoordinateAttributeName()); |
275 | m_texCoordAttribute->setVertexBaseType(QAttribute::Float); |
276 | m_texCoordAttribute->setVertexSize(2); |
277 | m_texCoordAttribute->setAttributeType(QAttribute::VertexAttribute); |
278 | m_texCoordAttribute->setBuffer(m_vertexBuffer); |
279 | m_texCoordAttribute->setByteStride(stride); |
280 | m_texCoordAttribute->setByteOffset(3 * sizeof(float)); |
281 | m_texCoordAttribute->setCount(nVerts); |
282 | |
283 | m_normalAttribute->setName(QAttribute::defaultNormalAttributeName()); |
284 | m_normalAttribute->setVertexBaseType(QAttribute::Float); |
285 | m_normalAttribute->setVertexSize(3); |
286 | m_normalAttribute->setAttributeType(QAttribute::VertexAttribute); |
287 | m_normalAttribute->setBuffer(m_vertexBuffer); |
288 | m_normalAttribute->setByteStride(stride); |
289 | m_normalAttribute->setByteOffset(5 * sizeof(float)); |
290 | m_normalAttribute->setCount(nVerts); |
291 | |
292 | m_tangentAttribute->setName(QAttribute::defaultTangentAttributeName()); |
293 | m_tangentAttribute->setVertexBaseType(QAttribute::Float); |
294 | m_tangentAttribute->setVertexSize(4); |
295 | m_tangentAttribute->setAttributeType(QAttribute::VertexAttribute); |
296 | m_tangentAttribute->setBuffer(m_vertexBuffer); |
297 | m_tangentAttribute->setByteStride(stride); |
298 | m_tangentAttribute->setByteOffset(8 * sizeof(float)); |
299 | m_tangentAttribute->setCount(nVerts); |
300 | |
301 | m_indexAttribute->setAttributeType(QAttribute::IndexAttribute); |
302 | m_indexAttribute->setVertexBaseType(QAttribute::UnsignedShort); |
303 | m_indexAttribute->setBuffer(m_indexBuffer); |
304 | |
305 | m_indexAttribute->setCount(faces * 3); |
306 | |
307 | m_vertexBuffer->setDataGenerator(QSharedPointer<SphereVertexDataFunctor>::create(arguments&: m_rings, arguments&: m_slices, arguments&: m_radius)); |
308 | m_indexBuffer->setDataGenerator(QSharedPointer<SphereIndexDataFunctor>::create(arguments&: m_rings, arguments&: m_slices)); |
309 | |
310 | q->addAttribute(attribute: m_positionAttribute); |
311 | q->addAttribute(attribute: m_texCoordAttribute); |
312 | q->addAttribute(attribute: m_normalAttribute); |
313 | if (m_generateTangents) |
314 | q->addAttribute(attribute: m_tangentAttribute); |
315 | q->addAttribute(attribute: m_indexAttribute); |
316 | } |
317 | |
318 | /*! |
319 | * \qmltype SphereGeometry |
320 | * \instantiates Qt3DExtras::QSphereGeometry |
321 | * \inqmlmodule Qt3D.Extras |
322 | * \brief SphereGeometry allows creation of a sphere in 3D space. |
323 | * |
324 | * The SphereGeometry type is most commonly used internally by the SphereMesh type |
325 | * but can also be used in custom GeometryRenderer types. |
326 | */ |
327 | |
328 | /*! |
329 | * \qmlproperty int SphereGeometry::rings |
330 | * |
331 | * Holds the number of rings in the sphere. |
332 | */ |
333 | |
334 | /*! |
335 | * \qmlproperty int SphereGeometry::slices |
336 | * |
337 | * Holds the number of slices in the sphere. |
338 | */ |
339 | |
340 | /*! |
341 | * \qmlproperty real SphereGeometry::radius |
342 | * |
343 | * Holds the radius of the sphere. |
344 | */ |
345 | |
346 | /*! |
347 | * \qmlproperty bool SphereGeometry::generateTangents |
348 | * |
349 | * Holds the value of the automatic tangent vectors generation flag. |
350 | * Tangent vectors are orthogonal to normal vectors. |
351 | */ |
352 | |
353 | /*! |
354 | * \qmlproperty Attribute SphereGeometry::positionAttribute |
355 | * |
356 | * Holds the geometry position attribute. |
357 | */ |
358 | |
359 | /*! |
360 | * \qmlproperty Attribute SphereGeometry::normalAttribute |
361 | * |
362 | * Holds the geometry normal attribute. |
363 | */ |
364 | |
365 | /*! |
366 | * \qmlproperty Attribute SphereGeometry::texCoordAttribute |
367 | * |
368 | * Holds the geometry texture coordinate attribute. |
369 | */ |
370 | |
371 | /*! |
372 | * \qmlproperty Attribute SphereGeometry::tangentAttribute |
373 | * |
374 | * Holds the geometry tangent attribute. |
375 | */ |
376 | |
377 | /*! |
378 | * \qmlproperty Attribute SphereGeometry::indexAttribute |
379 | * |
380 | * Holds the geometry index attribute. |
381 | */ |
382 | |
383 | /*! |
384 | * \class Qt3DExtras::QSphereGeometry |
385 | \ingroup qt3d-extras-geometries |
386 | * \inheaderfile Qt3DExtras/QSphereGeometry |
387 | * \inmodule Qt3DExtras |
388 | * \brief The QSphereGeometry class allows creation of a sphere in 3D space. |
389 | * \since 5.7 |
390 | * \ingroup geometries |
391 | * \inherits Qt3DRender::QGeometry |
392 | * |
393 | * The QSphereGeometry class is most commonly used internally by the QSphereMesh |
394 | * but can also be used in custom Qt3DRender::QGeometryRenderer subclasses. |
395 | */ |
396 | |
397 | /*! |
398 | * Constructs a new QSphereGeometry with \a parent. |
399 | */ |
400 | QSphereGeometry::(QNode *parent) |
401 | : QGeometry(*new QSphereGeometryPrivate(), parent) |
402 | { |
403 | Q_D(QSphereGeometry); |
404 | d->init(); |
405 | } |
406 | |
407 | /*! |
408 | * \internal |
409 | */ |
410 | QSphereGeometry::(QSphereGeometryPrivate &dd, QNode *parent) |
411 | : QGeometry(dd, parent) |
412 | { |
413 | Q_D(QSphereGeometry); |
414 | d->init(); |
415 | } |
416 | |
417 | /*! |
418 | * \internal |
419 | */ |
420 | QSphereGeometry::() |
421 | { |
422 | } |
423 | |
424 | /*! |
425 | * Updates vertices based on rings, slices, and radius properties |
426 | */ |
427 | void QSphereGeometry::() |
428 | { |
429 | Q_D(QSphereGeometry); |
430 | const int nVerts = (d->m_slices + 1) * (d->m_rings + 1); |
431 | d->m_positionAttribute->setCount(nVerts); |
432 | d->m_texCoordAttribute->setCount(nVerts); |
433 | d->m_normalAttribute->setCount(nVerts); |
434 | d->m_tangentAttribute->setCount(nVerts); |
435 | d->m_vertexBuffer->setDataGenerator(QSharedPointer<SphereVertexDataFunctor>::create(arguments&: d->m_rings, arguments&: d->m_slices, arguments&: d->m_radius)); |
436 | } |
437 | |
438 | /*! |
439 | * Updates indices based on rings and slices properties. |
440 | */ |
441 | void QSphereGeometry::() |
442 | { |
443 | Q_D(QSphereGeometry); |
444 | const int faces = (d->m_slices * 2) * (d->m_rings - 2) + (2 * d->m_slices); |
445 | d->m_indexAttribute->setCount(faces * 3); |
446 | d->m_indexBuffer->setDataGenerator(QSharedPointer<SphereIndexDataFunctor>::create(arguments&: d->m_rings, arguments&: d->m_slices)); |
447 | |
448 | } |
449 | |
450 | void QSphereGeometry::(int rings) |
451 | { |
452 | Q_D(QSphereGeometry); |
453 | if (rings != d->m_rings) { |
454 | d->m_rings = rings; |
455 | updateVertices(); |
456 | updateIndices(); |
457 | emit ringsChanged(rings); |
458 | } |
459 | } |
460 | |
461 | void QSphereGeometry::(int slices) |
462 | { |
463 | Q_D(QSphereGeometry); |
464 | if (slices != d->m_slices) { |
465 | d->m_slices = slices; |
466 | updateVertices(); |
467 | updateIndices(); |
468 | emit slicesChanged(slices); |
469 | } |
470 | } |
471 | |
472 | void QSphereGeometry::(float radius) |
473 | { |
474 | Q_D(QSphereGeometry); |
475 | if (radius != d->m_radius) { |
476 | d->m_radius = radius; |
477 | updateVertices(); |
478 | emit radiusChanged(radius); |
479 | } |
480 | } |
481 | |
482 | void QSphereGeometry::(bool gen) |
483 | { |
484 | Q_D(QSphereGeometry); |
485 | if (d->m_generateTangents != gen) { |
486 | if (d->m_generateTangents) |
487 | removeAttribute(attribute: d->m_tangentAttribute); |
488 | d->m_generateTangents = gen; |
489 | if (d->m_generateTangents) |
490 | addAttribute(attribute: d->m_tangentAttribute); |
491 | emit generateTangentsChanged(generateTangents: gen); |
492 | } |
493 | } |
494 | |
495 | /*! |
496 | * \property QSphereGeometry::generateTangents |
497 | * |
498 | * Holds the value of the automatic tangent vectors generation flag. |
499 | * Tangent vectors are orthogonal to normal vectors. |
500 | */ |
501 | bool QSphereGeometry::() const |
502 | { |
503 | Q_D(const QSphereGeometry); |
504 | return d->m_generateTangents; |
505 | } |
506 | |
507 | /*! |
508 | * \property QSphereGeometry::rings |
509 | * |
510 | * Holds the number of rings in the sphere. |
511 | */ |
512 | int QSphereGeometry::() const |
513 | { |
514 | Q_D(const QSphereGeometry); |
515 | return d->m_rings; |
516 | } |
517 | |
518 | /*! |
519 | * \property QSphereGeometry::slices |
520 | * |
521 | * Holds the number of slices in the sphere. |
522 | */ |
523 | int QSphereGeometry::() const |
524 | { |
525 | Q_D(const QSphereGeometry); |
526 | return d->m_slices; |
527 | } |
528 | |
529 | /*! |
530 | * \property QSphereGeometry::radius |
531 | * |
532 | * Holds the radius of the sphere. |
533 | */ |
534 | float QSphereGeometry::() const |
535 | { |
536 | Q_D(const QSphereGeometry); |
537 | return d->m_radius; |
538 | } |
539 | |
540 | /*! |
541 | * \property QSphereGeometry::positionAttribute |
542 | * |
543 | * Holds the geometry position attribute. |
544 | */ |
545 | QAttribute *QSphereGeometry::() const |
546 | { |
547 | Q_D(const QSphereGeometry); |
548 | return d->m_positionAttribute; |
549 | } |
550 | |
551 | /*! |
552 | * \property QSphereGeometry::normalAttribute |
553 | * |
554 | * Holds the geometry normal attribute. |
555 | */ |
556 | QAttribute *QSphereGeometry::() const |
557 | { |
558 | Q_D(const QSphereGeometry); |
559 | return d->m_normalAttribute; |
560 | } |
561 | |
562 | /*! |
563 | * \property QSphereGeometry::texCoordAttribute |
564 | * |
565 | * Holds the geometry texture coordinate attribute. |
566 | */ |
567 | QAttribute *QSphereGeometry::() const |
568 | { |
569 | Q_D(const QSphereGeometry); |
570 | return d->m_texCoordAttribute; |
571 | } |
572 | |
573 | /*! |
574 | * \property QSphereGeometry::tangentAttribute |
575 | * |
576 | * Holds the geometry tangent attribute. |
577 | */ |
578 | QAttribute *QSphereGeometry::() const |
579 | { |
580 | Q_D(const QSphereGeometry); |
581 | return d->m_tangentAttribute; |
582 | } |
583 | |
584 | /*! |
585 | * \property QSphereGeometry::indexAttribute |
586 | * |
587 | * Holds the geometry index attribute. |
588 | */ |
589 | QAttribute *QSphereGeometry::() const |
590 | { |
591 | Q_D(const QSphereGeometry); |
592 | return d->m_indexAttribute; |
593 | } |
594 | |
595 | } // Qt3DExtras |
596 | |
597 | QT_END_NAMESPACE |
598 | |
599 | |