blob: bf5651c9c50c3602f05bbdacfb44a1046dba9876 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QTest>
#include <QObject>
#include <Qt3DExtras/qtorusgeometry.h>
#include <Qt3DRender/qattribute.h>
#include <Qt3DRender/qbuffer.h>
#include <Qt3DRender/qbufferdatagenerator.h>
#include <qopenglcontext.h>
#include <QtGui/qvector2d.h>
#include <QtGui/qvector3d.h>
#include <QtGui/qvector4d.h>
#include <QtCore/qdebug.h>
#include <QtCore/qsharedpointer.h>
#include <QSignalSpy>
#include <qmath.h>
#include "geometrytesthelper.h"
class tst_QTorusGeometry : public QObject
{
Q_OBJECT
private Q_SLOTS:
void defaultConstruction()
{
// WHEN
Qt3DExtras::QTorusGeometry geometry;
// THEN
QCOMPARE(geometry.rings(), 16);
QCOMPARE(geometry.slices(), 16);
QCOMPARE(geometry.radius(), 1.0f);
QCOMPARE(geometry.minorRadius(), 1.0f);
QVERIFY(geometry.positionAttribute() != nullptr);
QCOMPARE(geometry.positionAttribute()->name(), Qt3DRender::QAttribute::defaultPositionAttributeName());
QVERIFY(geometry.normalAttribute() != nullptr);
QCOMPARE(geometry.normalAttribute()->name(), Qt3DRender::QAttribute::defaultNormalAttributeName());
QVERIFY(geometry.texCoordAttribute() != nullptr);
QCOMPARE(geometry.texCoordAttribute()->name(), Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName());
// TODO: Expose tangent attribute in Qt 5.8 and see below
// QVERIFY(geometry.tangentAttribute() != nullptr);
// QCOMPARE(geometry.tangentAttribute()->name(), Qt3DRender::QAttribute::defaultTangentAttributeName());
QVERIFY(geometry.indexAttribute() != nullptr);
}
void properties()
{
// GIVEN
Qt3DExtras::QTorusGeometry geometry;
{
// WHEN
QSignalSpy spy(&geometry, SIGNAL(ringsChanged(int)));
const int newValue = 20;
geometry.setRings(newValue);
// THEN
QCOMPARE(geometry.rings(), newValue);
QCOMPARE(spy.count(), 1);
// WHEN
spy.clear();
geometry.setRings(newValue);
// THEN
QCOMPARE(geometry.rings(), newValue);
QCOMPARE(spy.count(), 0);
}
{
// WHEN
QSignalSpy spy(&geometry, SIGNAL(slicesChanged(int)));
const int newValue = 2.0f;
geometry.setSlices(newValue);
// THEN
QCOMPARE(geometry.slices(), newValue);
QCOMPARE(spy.count(), 1);
// WHEN
spy.clear();
geometry.setSlices(newValue);
// THEN
QCOMPARE(geometry.slices(), newValue);
QCOMPARE(spy.count(), 0);
}
{
// WHEN
QSignalSpy spy(&geometry, SIGNAL(radiusChanged(float)));
const float newValue = 2.0f;
geometry.setRadius(newValue);
// THEN
QCOMPARE(geometry.radius(), newValue);
QCOMPARE(spy.count(), 1);
// WHEN
spy.clear();
geometry.setRadius(newValue);
// THEN
QCOMPARE(geometry.radius(), newValue);
QCOMPARE(spy.count(), 0);
}
{
// WHEN
QSignalSpy spy(&geometry, SIGNAL(minorRadiusChanged(float)));
const float newValue = 0.25f;
geometry.setMinorRadius(newValue);
// THEN
QCOMPARE(geometry.minorRadius(), newValue);
QCOMPARE(spy.count(), 1);
// WHEN
spy.clear();
geometry.setMinorRadius(newValue);
// THEN
QCOMPARE(geometry.minorRadius(), newValue);
QCOMPARE(spy.count(), 0);
}
}
void generatedGeometryShouldBeConsistent_data()
{
QTest::addColumn<int>("rings");
QTest::addColumn<int>("slices");
QTest::addColumn<float>("radius");
QTest::addColumn<float>("minorRadius");
QTest::addColumn<int>("triangleIndex");
QTest::addColumn<QVector<quint16>>("indices");
QTest::addColumn<QVector<QVector3D>>("positions");
QTest::addColumn<QVector<QVector3D>>("normals");
QTest::addColumn<QVector<QVector2D>>("texCoords");
QTest::addColumn<QVector<QVector4D>>("tangents");
{
// Torus properties
const int rings = 8;
const int slices = 8;
const float radius = 2.0f;
const float minorRadius = 0.5f;
// Angular factors for the vertices:
// u iterates around the major radius
// v iterates around the minor radius (around each ring)
const float du = float(2.0 * M_PI / rings);
const float dv = float(2.0 * M_PI / slices);
const float u0 = 0.0f;
const float u1 = du;
const float v0 = 0.0f;
const float v1 = dv;
const float cosu0 = float(qCos(u0));
const float sinu0 = float(qSin(u0));
const float cosu1 = float(qCos(u1));
const float sinu1 = float(qSin(u1));
const float cosv0 = float(qCos(v0 + M_PI)); // Seam is on inner edge
const float sinv0 = float(qSin(v0));
const float cosv1 = float(qCos(v1 + M_PI));
const float sinv1 = float(qSin(v1));
// The triangle and indices
const int triangleIndex = 0;
const auto indices = (QVector<quint16>() << 0 << 1 << 9);
// Calculate attributes for vertices A, B, and C of the triangle
const float rA = radius + minorRadius * cosv0;
const float rB = radius + minorRadius * cosv1;
const float rC = radius + minorRadius * cosv0;
const auto posA = QVector3D(rA * cosu0, rA * sinu0, minorRadius * sinv0);
const auto posB = QVector3D(rB * cosu0, rB * sinu0, minorRadius * sinv1);
const auto posC = QVector3D(rC * cosu1, rC * sinu1, minorRadius * sinv0);
const auto positions = (QVector<QVector3D>() << posA << posB << posC);
const auto nA = QVector3D(cosv0 * cosu0, cosv0 * sinu0, sinv0).normalized();
const auto nB = QVector3D(cosv1 * cosu0, cosv1 * sinu0, sinv1).normalized();
const auto nC = QVector3D(cosv0 * cosu1, cosv0 * sinu1, sinv0).normalized();
const auto normals = (QVector<QVector3D>() << nA << nB << nC);
const auto tcA = QVector2D(u0, v0) / float(2.0 * M_PI);
const auto tcB = QVector2D(u0, v1) / float(2.0 * M_PI);
const auto tcC = QVector2D(u1, v0) / float(2.0 * M_PI);
const auto texCoords = (QVector<QVector2D>() << tcA << tcB << tcC);
const auto tA = QVector4D(-sinu0, cosu0, 0.0f, 1.0f);
const auto tB = QVector4D(-sinu0, cosu0, 0.0f, 1.0f);
const auto tC = QVector4D(-sinu1, cosu1, 0.0f, 1.0f);
const auto tangents = (QVector<QVector4D>() << tA << tB << tC);
// Add the row
QTest::newRow("8rings_8slices_firstTriangle")
<< rings << slices << radius << minorRadius
<< triangleIndex
<< indices << positions << normals << texCoords << tangents;
}
{
// Note: The vertices used in this test case are different than the
// ones above. So, we cannot abstract this into a function easily.
// Here we use the 2nd triangle in a rectangular face, the test above
// uses the first triangle in the rectangular face.
// Torus properties
const int rings = 8;
const int slices = 8;
const float radius = 2.0f;
const float minorRadius = 0.5f;
// Angular factors for the vertices:
// u iterates around the major radius
// v iterates around the minor radius (around each ring)
const float du = float(2.0 * M_PI / rings);
const float dv = float(2.0 * M_PI / slices);
const float u0 = 7.0f * du;
const float u1 = float(2.0 * M_PI);
const float v0 = 7.0f * dv;
const float v1 = float(2.0 * M_PI);
const float cosu0 = float(qCos(u0));
const float sinu0 = float(qSin(u0));
const float cosu1 = float(qCos(u1));
const float sinu1 = float(qSin(u1));
const float cosv0 = float(qCos(v0 + M_PI)); // Seam is on inner edge
const float sinv0 = float(qSin(v0));
const float cosv1 = float(qCos(v1 + M_PI));
const float sinv1 = float(qSin(v1));
// The triangle and indices
const int triangleIndex = 127;
const auto indices = (QVector<quint16>() << 71 << 80 << 79);
// Calculate attributes for vertices A, B, and C of the triangle
const float rA = radius + minorRadius * cosv1;
const float rB = radius + minorRadius * cosv1;
const float rC = radius + minorRadius * cosv0;
const auto posA = QVector3D(rA * cosu0, rA * sinu0, minorRadius * sinv1);
const auto posB = QVector3D(rB * cosu1, rB * sinu1, minorRadius * sinv1);
const auto posC = QVector3D(rC * cosu1, rC * sinu1, minorRadius * sinv0);
const auto positions = (QVector<QVector3D>() << posA << posB << posC);
const auto nA = QVector3D(cosv1 * cosu0, cosv1 * sinu0, sinv1).normalized();
const auto nB = QVector3D(cosv1 * cosu1, cosv1 * sinu1, sinv1).normalized();
const auto nC = QVector3D(cosv0 * cosu1, cosv0 * sinu1, sinv0).normalized();
const auto normals = (QVector<QVector3D>() << nA << nB << nC);
const auto tcA = QVector2D(u0, v1) / float(2.0 * M_PI);
const auto tcB = QVector2D(u1, v1) / float(2.0 * M_PI);
const auto tcC = QVector2D(u1, v0) / float(2.0 * M_PI);
const auto texCoords = (QVector<QVector2D>() << tcA << tcB << tcC);
const auto tA = QVector4D(-sinu0, cosu1, 0.0f, 1.0f);
const auto tB = QVector4D(-sinu1, cosu1, 0.0f, 1.0f);
const auto tC = QVector4D(-sinu1, cosu1, 0.0f, 1.0f);
const auto tangents = (QVector<QVector4D>() << tA << tB << tC);
// Add the row
QTest::newRow("8rings_8slices_lastTriangle")
<< rings << slices << radius << minorRadius
<< triangleIndex
<< indices << positions << normals << texCoords << tangents;
}
}
void generatedGeometryShouldBeConsistent()
{
// GIVEN
Qt3DExtras::QTorusGeometry geometry;
const QVector<Qt3DRender::QAttribute *> attributes = geometry.attributes();
Qt3DRender::QAttribute *positionAttribute = geometry.positionAttribute();
Qt3DRender::QAttribute *normalAttribute = geometry.normalAttribute();
Qt3DRender::QAttribute *texCoordAttribute = geometry.texCoordAttribute();
// Qt3DRender::QAttribute *tangentAttribute = geometry.tangentAttribute();
Qt3DRender::QAttribute *indexAttribute = geometry.indexAttribute();
// WHEN
QFETCH(int, rings);
QFETCH(int, slices);
QFETCH(float, radius);
QFETCH(float, minorRadius);
geometry.setRings(rings);
geometry.setSlices(slices);
geometry.setRadius(radius);
geometry.setMinorRadius(minorRadius);
generateGeometry(geometry);
// THEN
// Check buffer of each attribute is valid and actually has some data
for (const auto &attribute : attributes) {
Qt3DRender::QBuffer *buffer = attribute->buffer();
QVERIFY(buffer != nullptr);
QVERIFY(buffer->data().size() != 0);
}
// Check some data in the buffers
// Check specific indices and vertex attributes of triangle under test
QFETCH(int, triangleIndex);
QFETCH(QVector<quint16>, indices);
QFETCH(QVector<QVector3D>, positions);
QFETCH(QVector<QVector3D>, normals);
QFETCH(QVector<QVector2D>, texCoords);
// QFETCH(QVector<QVector4D>, tangents);
int i = 0;
for (auto index : indices) {
const auto testIndex = extractIndexData<quint16>(indexAttribute, 3 * triangleIndex + i);
QCOMPARE(testIndex, indices.at(i));
const auto position = extractVertexData<QVector3D, quint32>(positionAttribute, index);
QVERIFY(qFuzzyCompare(position, positions.at(i)));
const auto normal = extractVertexData<QVector3D, quint32>(normalAttribute, index);
QVERIFY(qFuzzyCompare(normal, normals.at(i)));
const auto texCoord = extractVertexData<QVector2D, quint32>(texCoordAttribute, index);
QVERIFY(qFuzzyCompare(texCoord, texCoords.at(i)));
// const auto tangent = extractVertexData<QVector4D, quint32>(tangentAttribute, index);
// QVERIFY(qFuzzyCompare(tangent, tangents.at(i)));
++i;
}
}
};
QTEST_APPLESS_MAIN(tst_QTorusGeometry)
#include "tst_qtorusgeometry.moc"