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:GPL-EXCEPT$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QTest>
30#include <QObject>
31#include <Qt3DExtras/qtorusgeometry.h>
32#include <Qt3DRender/qattribute.h>
33#include <Qt3DRender/qbuffer.h>
34#include <Qt3DRender/qbufferdatagenerator.h>
35#include <qopenglcontext.h>
36#include <QtGui/qvector2d.h>
37#include <QtGui/qvector3d.h>
38#include <QtGui/qvector4d.h>
39#include <QtCore/qdebug.h>
40#include <QtCore/qsharedpointer.h>
41#include <QSignalSpy>
42#include <qmath.h>
43
44#include "geometrytesthelper.h"
45
46class tst_QTorusGeometry : public QObject
47{
48 Q_OBJECT
49private Q_SLOTS:
50 void defaultConstruction()
51 {
52 // WHEN
53 Qt3DExtras::QTorusGeometry geometry;
54
55 // THEN
56 QCOMPARE(geometry.rings(), 16);
57 QCOMPARE(geometry.slices(), 16);
58 QCOMPARE(geometry.radius(), 1.0f);
59 QCOMPARE(geometry.minorRadius(), 1.0f);
60 QVERIFY(geometry.positionAttribute() != nullptr);
61 QCOMPARE(geometry.positionAttribute()->name(), Qt3DRender::QAttribute::defaultPositionAttributeName());
62 QVERIFY(geometry.normalAttribute() != nullptr);
63 QCOMPARE(geometry.normalAttribute()->name(), Qt3DRender::QAttribute::defaultNormalAttributeName());
64 QVERIFY(geometry.texCoordAttribute() != nullptr);
65 QCOMPARE(geometry.texCoordAttribute()->name(), Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName());
66 // TODO: Expose tangent attribute in Qt 5.8 and see below
67// QVERIFY(geometry.tangentAttribute() != nullptr);
68// QCOMPARE(geometry.tangentAttribute()->name(), Qt3DRender::QAttribute::defaultTangentAttributeName());
69 QVERIFY(geometry.indexAttribute() != nullptr);
70 }
71
72 void properties()
73 {
74 // GIVEN
75 Qt3DExtras::QTorusGeometry geometry;
76
77 {
78 // WHEN
79 QSignalSpy spy(&geometry, SIGNAL(ringsChanged(int)));
80 const int newValue = 20;
81 geometry.setRings(newValue);
82
83 // THEN
84 QCOMPARE(geometry.rings(), newValue);
85 QCOMPARE(spy.count(), 1);
86
87 // WHEN
88 spy.clear();
89 geometry.setRings(newValue);
90
91 // THEN
92 QCOMPARE(geometry.rings(), newValue);
93 QCOMPARE(spy.count(), 0);
94 }
95
96 {
97 // WHEN
98 QSignalSpy spy(&geometry, SIGNAL(slicesChanged(int)));
99 const int newValue = 2.0f;
100 geometry.setSlices(newValue);
101
102 // THEN
103 QCOMPARE(geometry.slices(), newValue);
104 QCOMPARE(spy.count(), 1);
105
106 // WHEN
107 spy.clear();
108 geometry.setSlices(newValue);
109
110 // THEN
111 QCOMPARE(geometry.slices(), newValue);
112 QCOMPARE(spy.count(), 0);
113 }
114
115 {
116 // WHEN
117 QSignalSpy spy(&geometry, SIGNAL(radiusChanged(float)));
118 const float newValue = 2.0f;
119 geometry.setRadius(newValue);
120
121 // THEN
122 QCOMPARE(geometry.radius(), newValue);
123 QCOMPARE(spy.count(), 1);
124
125 // WHEN
126 spy.clear();
127 geometry.setRadius(newValue);
128
129 // THEN
130 QCOMPARE(geometry.radius(), newValue);
131 QCOMPARE(spy.count(), 0);
132 }
133
134 {
135 // WHEN
136 QSignalSpy spy(&geometry, SIGNAL(minorRadiusChanged(float)));
137 const float newValue = 0.25f;
138 geometry.setMinorRadius(newValue);
139
140 // THEN
141 QCOMPARE(geometry.minorRadius(), newValue);
142 QCOMPARE(spy.count(), 1);
143
144 // WHEN
145 spy.clear();
146 geometry.setMinorRadius(newValue);
147
148 // THEN
149 QCOMPARE(geometry.minorRadius(), newValue);
150 QCOMPARE(spy.count(), 0);
151 }
152 }
153
154 void generatedGeometryShouldBeConsistent_data()
155 {
156 QTest::addColumn<int>(name: "rings");
157 QTest::addColumn<int>(name: "slices");
158 QTest::addColumn<float>(name: "radius");
159 QTest::addColumn<float>(name: "minorRadius");
160 QTest::addColumn<int>(name: "triangleIndex");
161 QTest::addColumn<QVector<quint16>>(name: "indices");
162 QTest::addColumn<QVector<QVector3D>>(name: "positions");
163 QTest::addColumn<QVector<QVector3D>>(name: "normals");
164 QTest::addColumn<QVector<QVector2D>>(name: "texCoords");
165 QTest::addColumn<QVector<QVector4D>>(name: "tangents");
166
167 {
168 // Torus properties
169 const int rings = 8;
170 const int slices = 8;
171 const float radius = 2.0f;
172 const float minorRadius = 0.5f;
173
174 // Angular factors for the vertices:
175 // u iterates around the major radius
176 // v iterates around the minor radius (around each ring)
177 const float du = float(2.0 * M_PI / rings);
178 const float dv = float(2.0 * M_PI / slices);
179 const float u0 = 0.0f;
180 const float u1 = du;
181 const float v0 = 0.0f;
182 const float v1 = dv;
183
184 const float cosu0 = float(qCos(v: u0));
185 const float sinu0 = float(qSin(v: u0));
186 const float cosu1 = float(qCos(v: u1));
187 const float sinu1 = float(qSin(v: u1));
188
189 const float cosv0 = float(qCos(v: v0 + M_PI)); // Seam is on inner edge
190 const float sinv0 = float(qSin(v: v0));
191 const float cosv1 = float(qCos(v: v1 + M_PI));
192 const float sinv1 = float(qSin(v: v1));
193
194 // The triangle and indices
195 const int triangleIndex = 0;
196 const auto indices = (QVector<quint16>() << 0 << 1 << 9);
197
198 // Calculate attributes for vertices A, B, and C of the triangle
199 const float rA = radius + minorRadius * cosv0;
200 const float rB = radius + minorRadius * cosv1;
201 const float rC = radius + minorRadius * cosv0;
202
203 const auto posA = QVector3D(rA * cosu0, rA * sinu0, minorRadius * sinv0);
204 const auto posB = QVector3D(rB * cosu0, rB * sinu0, minorRadius * sinv1);
205 const auto posC = QVector3D(rC * cosu1, rC * sinu1, minorRadius * sinv0);
206 const auto positions = (QVector<QVector3D>() << posA << posB << posC);
207
208 const auto nA = QVector3D(cosv0 * cosu0, cosv0 * sinu0, sinv0).normalized();
209 const auto nB = QVector3D(cosv1 * cosu0, cosv1 * sinu0, sinv1).normalized();
210 const auto nC = QVector3D(cosv0 * cosu1, cosv0 * sinu1, sinv0).normalized();
211 const auto normals = (QVector<QVector3D>() << nA << nB << nC);
212
213 const auto tcA = QVector2D(u0, v0) / float(2.0 * M_PI);
214 const auto tcB = QVector2D(u0, v1) / float(2.0 * M_PI);
215 const auto tcC = QVector2D(u1, v0) / float(2.0 * M_PI);
216 const auto texCoords = (QVector<QVector2D>() << tcA << tcB << tcC);
217
218 const auto tA = QVector4D(-sinu0, cosu0, 0.0f, 1.0f);
219 const auto tB = QVector4D(-sinu0, cosu0, 0.0f, 1.0f);
220 const auto tC = QVector4D(-sinu1, cosu1, 0.0f, 1.0f);
221 const auto tangents = (QVector<QVector4D>() << tA << tB << tC);
222
223 // Add the row
224 QTest::newRow(dataTag: "8rings_8slices_firstTriangle")
225 << rings << slices << radius << minorRadius
226 << triangleIndex
227 << indices << positions << normals << texCoords << tangents;
228 }
229
230 {
231 // Note: The vertices used in this test case are different than the
232 // ones above. So, we cannot abstract this into a function easily.
233 // Here we use the 2nd triangle in a rectangular face, the test above
234 // uses the first triangle in the rectangular face.
235
236 // Torus properties
237 const int rings = 8;
238 const int slices = 8;
239 const float radius = 2.0f;
240 const float minorRadius = 0.5f;
241
242 // Angular factors for the vertices:
243 // u iterates around the major radius
244 // v iterates around the minor radius (around each ring)
245 const float du = float(2.0 * M_PI / rings);
246 const float dv = float(2.0 * M_PI / slices);
247 const float u0 = 7.0f * du;
248 const float u1 = float(2.0 * M_PI);
249 const float v0 = 7.0f * dv;
250 const float v1 = float(2.0 * M_PI);
251
252 const float cosu0 = float(qCos(v: u0));
253 const float sinu0 = float(qSin(v: u0));
254 const float cosu1 = float(qCos(v: u1));
255 const float sinu1 = float(qSin(v: u1));
256
257 const float cosv0 = float(qCos(v: v0 + M_PI)); // Seam is on inner edge
258 const float sinv0 = float(qSin(v: v0));
259 const float cosv1 = float(qCos(v: v1 + M_PI));
260 const float sinv1 = float(qSin(v: v1));
261
262 // The triangle and indices
263 const int triangleIndex = 127;
264 const auto indices = (QVector<quint16>() << 71 << 80 << 79);
265
266 // Calculate attributes for vertices A, B, and C of the triangle
267 const float rA = radius + minorRadius * cosv1;
268 const float rB = radius + minorRadius * cosv1;
269 const float rC = radius + minorRadius * cosv0;
270
271 const auto posA = QVector3D(rA * cosu0, rA * sinu0, minorRadius * sinv1);
272 const auto posB = QVector3D(rB * cosu1, rB * sinu1, minorRadius * sinv1);
273 const auto posC = QVector3D(rC * cosu1, rC * sinu1, minorRadius * sinv0);
274 const auto positions = (QVector<QVector3D>() << posA << posB << posC);
275
276 const auto nA = QVector3D(cosv1 * cosu0, cosv1 * sinu0, sinv1).normalized();
277 const auto nB = QVector3D(cosv1 * cosu1, cosv1 * sinu1, sinv1).normalized();
278 const auto nC = QVector3D(cosv0 * cosu1, cosv0 * sinu1, sinv0).normalized();
279 const auto normals = (QVector<QVector3D>() << nA << nB << nC);
280
281 const auto tcA = QVector2D(u0, v1) / float(2.0 * M_PI);
282 const auto tcB = QVector2D(u1, v1) / float(2.0 * M_PI);
283 const auto tcC = QVector2D(u1, v0) / float(2.0 * M_PI);
284 const auto texCoords = (QVector<QVector2D>() << tcA << tcB << tcC);
285
286 const auto tA = QVector4D(-sinu0, cosu1, 0.0f, 1.0f);
287 const auto tB = QVector4D(-sinu1, cosu1, 0.0f, 1.0f);
288 const auto tC = QVector4D(-sinu1, cosu1, 0.0f, 1.0f);
289 const auto tangents = (QVector<QVector4D>() << tA << tB << tC);
290
291 // Add the row
292 QTest::newRow(dataTag: "8rings_8slices_lastTriangle")
293 << rings << slices << radius << minorRadius
294 << triangleIndex
295 << indices << positions << normals << texCoords << tangents;
296 }
297 }
298
299 void generatedGeometryShouldBeConsistent()
300 {
301 // GIVEN
302 Qt3DExtras::QTorusGeometry geometry;
303 const QVector<Qt3DRender::QAttribute *> attributes = geometry.attributes();
304 Qt3DRender::QAttribute *positionAttribute = geometry.positionAttribute();
305 Qt3DRender::QAttribute *normalAttribute = geometry.normalAttribute();
306 Qt3DRender::QAttribute *texCoordAttribute = geometry.texCoordAttribute();
307// Qt3DRender::QAttribute *tangentAttribute = geometry.tangentAttribute();
308 Qt3DRender::QAttribute *indexAttribute = geometry.indexAttribute();
309
310 // WHEN
311 QFETCH(int, rings);
312 QFETCH(int, slices);
313 QFETCH(float, radius);
314 QFETCH(float, minorRadius);
315 geometry.setRings(rings);
316 geometry.setSlices(slices);
317 geometry.setRadius(radius);
318 geometry.setMinorRadius(minorRadius);
319
320 generateGeometry(geometry);
321
322 // THEN
323
324 // Check buffer of each attribute is valid and actually has some data
325 for (const auto &attribute : attributes) {
326 Qt3DRender::QBuffer *buffer = attribute->buffer();
327 QVERIFY(buffer != nullptr);
328 QVERIFY(buffer->data().size() != 0);
329 }
330
331 // Check some data in the buffers
332
333 // Check specific indices and vertex attributes of triangle under test
334 QFETCH(int, triangleIndex);
335 QFETCH(QVector<quint16>, indices);
336 QFETCH(QVector<QVector3D>, positions);
337 QFETCH(QVector<QVector3D>, normals);
338 QFETCH(QVector<QVector2D>, texCoords);
339// QFETCH(QVector<QVector4D>, tangents);
340
341 int i = 0;
342 for (auto index : indices) {
343 const auto testIndex = extractIndexData<quint16>(attribute: indexAttribute, index: 3 * triangleIndex + i);
344 QCOMPARE(testIndex, indices.at(i));
345
346 const auto position = extractVertexData<QVector3D, quint32>(attribute: positionAttribute, index);
347 QVERIFY(qFuzzyCompare(position, positions.at(i)));
348
349 const auto normal = extractVertexData<QVector3D, quint32>(attribute: normalAttribute, index);
350 QVERIFY(qFuzzyCompare(normal, normals.at(i)));
351
352 const auto texCoord = extractVertexData<QVector2D, quint32>(attribute: texCoordAttribute, index);
353 QVERIFY(qFuzzyCompare(texCoord, texCoords.at(i)));
354
355// const auto tangent = extractVertexData<QVector4D, quint32>(tangentAttribute, index);
356// QVERIFY(qFuzzyCompare(tangent, tangents.at(i)));
357
358 ++i;
359 }
360 }
361};
362
363
364QTEST_APPLESS_MAIN(tst_QTorusGeometry)
365
366#include "tst_qtorusgeometry.moc"
367

source code of qt3d/tests/auto/extras/qtorusgeometry/tst_qtorusgeometry.cpp