/**************************************************************************** ** ** 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 #include #include #include #include #include #include #include #include #include #include #include #include namespace { void generateGeometry(Qt3DRender::QGeometry &geometry) { // Get all attributes const QVector attributes = geometry.attributes(); // Get all unique data generators from the buffers referenced by the attributes QHash dataGenerators; for (const auto attribute : attributes) { const auto dataGenerator = attribute->buffer()->dataGenerator(); if (!dataGenerators.contains(dataGenerator)) dataGenerators.insert(dataGenerator, attribute->buffer()); } // Generate data for each buffer const auto end = dataGenerators.end(); for (auto it = dataGenerators.begin(); it != end; ++it) { Qt3DRender::QBufferDataGeneratorPtr dataGenerator = it.key(); const QByteArray data = (*dataGenerator)(); Qt3DRender::QBuffer *buffer = it.value(); buffer->setData(data); } } template IndexType extractIndexData(Qt3DRender::QAttribute *attribute, int index) { // Get the raw data const IndexType *typedData = reinterpret_cast(attribute->buffer()->data().constData()); // Offset into the data taking stride and offset into account const IndexType indexValue = *(typedData + index); return indexValue; } template VertexType extractVertexData(Qt3DRender::QAttribute *attribute, IndexType index) { // Get the raw data const char *rawData = attribute->buffer()->data().constData(); // Offset into the data taking stride and offset into account const char *vertexData = rawData + (index * attribute->byteStride() + attribute->byteOffset()); // Construct vertex from the typed data VertexType vertex; const Qt3DRender::QAttribute::VertexBaseType type = attribute->vertexBaseType(); switch (type) { case Qt3DRender::QAttribute::Float: { const float *typedVertexData = reinterpret_cast(vertexData); const int components = attribute->vertexSize(); for (int i = 0; i < components; ++i) vertex[i] = typedVertexData[i]; break; // TODO: Handle other types as needed } default: qWarning() << "Unhandled type"; Q_UNREACHABLE(); break; } return vertex; } } class tst_QCuboidGeometry : public QObject { Q_OBJECT private Q_SLOTS: void defaultConstruction() { // WHEN Qt3DExtras::QCuboidGeometry geometry; // THEN QCOMPARE(geometry.xExtent(), 1.0f); QCOMPARE(geometry.yExtent(), 1.0f); QCOMPARE(geometry.zExtent(), 1.0f); QCOMPARE(geometry.xyMeshResolution(), QSize(2, 2)); QCOMPARE(geometry.yzMeshResolution(), QSize(2, 2)); QCOMPARE(geometry.xzMeshResolution(), QSize(2, 2)); 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()); QVERIFY(geometry.tangentAttribute() != nullptr); QCOMPARE(geometry.tangentAttribute()->name(), Qt3DRender::QAttribute::defaultTangentAttributeName()); QVERIFY(geometry.indexAttribute() != nullptr); } void properties() { // GIVEN Qt3DExtras::QCuboidGeometry geometry; { // WHEN QSignalSpy spy(&geometry, SIGNAL(xExtentChanged(float))); const float newValue = 2.0f; geometry.setXExtent(newValue); // THEN QCOMPARE(geometry.xExtent(), newValue); QCOMPARE(spy.count(), 1); // WHEN spy.clear(); geometry.setXExtent(newValue); // THEN QCOMPARE(geometry.xExtent(), newValue); QCOMPARE(spy.count(), 0); } { // WHEN QSignalSpy spy(&geometry, SIGNAL(yExtentChanged(float))); const float newValue = 2.0f; geometry.setYExtent(newValue); // THEN QCOMPARE(geometry.yExtent(), newValue); QCOMPARE(spy.count(), 1); // WHEN spy.clear(); geometry.setYExtent(newValue); // THEN QCOMPARE(geometry.yExtent(), newValue); QCOMPARE(spy.count(), 0); } { // WHEN QSignalSpy spy(&geometry, SIGNAL(zExtentChanged(float))); const float newValue = 2.0f; geometry.setZExtent(newValue); // THEN QCOMPARE(geometry.zExtent(), newValue); QCOMPARE(spy.count(), 1); // WHEN spy.clear(); geometry.setZExtent(newValue); // THEN QCOMPARE(geometry.zExtent(), newValue); QCOMPARE(spy.count(), 0); } { // WHEN QSignalSpy spy(&geometry, SIGNAL(xyMeshResolutionChanged(QSize))); const auto newValue = QSize(4, 8); geometry.setXYMeshResolution(newValue); // THEN QCOMPARE(geometry.xyMeshResolution(), newValue); QCOMPARE(spy.count(), 1); // WHEN spy.clear(); geometry.setXYMeshResolution(newValue); // THEN QCOMPARE(geometry.xyMeshResolution(), newValue); QCOMPARE(spy.count(), 0); } { // WHEN QSignalSpy spy(&geometry, SIGNAL(yzMeshResolutionChanged(QSize))); const auto newValue = QSize(4, 8); geometry.setYZMeshResolution(newValue); // THEN QCOMPARE(geometry.yzMeshResolution(), newValue); QCOMPARE(spy.count(), 1); // WHEN spy.clear(); geometry.setYZMeshResolution(newValue); // THEN QCOMPARE(geometry.yzMeshResolution(), newValue); QCOMPARE(spy.count(), 0); } { // WHEN QSignalSpy spy(&geometry, SIGNAL(xzMeshResolutionChanged(QSize))); const auto newValue = QSize(4, 8); geometry.setXZMeshResolution(newValue); // THEN QCOMPARE(geometry.xzMeshResolution(), newValue); QCOMPARE(spy.count(), 1); // WHEN spy.clear(); geometry.setXZMeshResolution(newValue); // THEN QCOMPARE(geometry.xzMeshResolution(), newValue); QCOMPARE(spy.count(), 0); } } void generatedGeometryShouldBeConsistent_data() { QTest::addColumn("xExtent"); QTest::addColumn("yExtent"); QTest::addColumn("zExtent"); QTest::addColumn("xyMeshResolution"); QTest::addColumn("yzMeshResolution"); QTest::addColumn("xzMeshResolution"); QTest::addColumn("triangleIndex"); QTest::addColumn>("indices"); QTest::addColumn>("positions"); QTest::addColumn>("normals"); QTest::addColumn>("texCoords"); QTest::addColumn>("tangents"); { const int triangleIndex = 0; const auto indices = (QVector() << 0 << 1 << 2); const auto positions = (QVector() << QVector3D(0.5f, -0.5f, -0.5f) << QVector3D(0.5f, 0.5f, -0.5f) << QVector3D(0.5f, -0.5f, 0.5f)); const auto normals = (QVector() << QVector3D(1.0f, 0.0f, 0.0f) << QVector3D(1.0f, 0.0f, 0.0f) << QVector3D(1.0f, 0.0f, 0.0f)); const auto texCoords = (QVector() << QVector2D(1.0f, 0.0f) << QVector2D(1.0f, 1.0f) << QVector2D(0.0f, 0.0f)); const auto tangents = (QVector() << QVector4D(0.0f, 0.0f, -1.0f, 1.0f) << QVector4D(0.0f, 0.0f, -1.0f, 1.0f) << QVector4D(0.0f, 0.0f, -1.0f, 1.0f)); QTest::newRow("default_positiveX_firstTriangle") << 1.0f << 1.0f << 1.0f << QSize(2,2) << QSize(2,2) << QSize(2,2) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 3; const auto indices = (QVector() << 6 << 5 << 7); const auto positions = (QVector() << QVector3D(-0.5f, -0.5f, -0.5f) << QVector3D(-0.5f, 0.5f, 0.5f) << QVector3D(-0.5f, 0.5f, -0.5f)); const auto normals = (QVector() << QVector3D(-1.0f, 0.0f, 0.0f) << QVector3D(-1.0f, 0.0f, 0.0f) << QVector3D(-1.0f, 0.0f, 0.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 0.0f) << QVector2D(1.0f, 1.0f) << QVector2D(0.0f, 1.0f)); const auto tangents = (QVector() << QVector4D(0.0f, 0.0f, 1.0f, 1.0f) << QVector4D(0.0f, 0.0f, 1.0f, 1.0f) << QVector4D(0.0f, 0.0f, 1.0f, 1.0f)); QTest::newRow("default_negativeX_lastTriangle") << 1.0f << 1.0f << 1.0f << QSize(2,2) << QSize(2,2) << QSize(2,2) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 4; const auto indices = (QVector() << 8 << 9 << 10); const auto positions = (QVector() << QVector3D(-0.5f, 0.5f, 0.5f) << QVector3D(0.5f, 0.5f, 0.5f) << QVector3D(-0.5f, 0.5f, -0.5f)); const auto normals = (QVector() << QVector3D(0.0f, 1.0f, 0.0f) << QVector3D(0.0f, 1.0f, 0.0f) << QVector3D(0.0f, 1.0f, 0.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 0.0f) << QVector2D(1.0f, 0.0f) << QVector2D(0.0f, 1.0f)); const auto tangents = (QVector() << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f)); QTest::newRow("default_positiveY_firstTriangle") << 1.0f << 1.0f << 1.0f << QSize(2,2) << QSize(2,2) << QSize(2,2) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 7; const auto indices = (QVector() << 14 << 13 << 15); const auto positions = (QVector() << QVector3D(-0.5f, -0.5f, 0.5f) << QVector3D(0.5f, -0.5f, -0.5f) << QVector3D(0.5f, -0.5f, 0.5f)); const auto normals = (QVector() << QVector3D(0.0f, -1.0f, 0.0f) << QVector3D(0.0f, -1.0f, 0.0f) << QVector3D(0.0f, -1.0f, 0.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 1.0f) << QVector2D(1.0f, 0.0f) << QVector2D(1.0f, 1.0f)); const auto tangents = (QVector() << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f)); QTest::newRow("default_negativeY_lastTriangle") << 1.0f << 1.0f << 1.0f << QSize(2,2) << QSize(2,2) << QSize(2,2) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 8; const auto indices = (QVector() << 16 << 17 << 18); const auto positions = (QVector() << QVector3D(-0.5f, -0.5f, 0.5f) << QVector3D(0.5f, -0.5f, 0.5f) << QVector3D(-0.5f, 0.5f, 0.5f)); const auto normals = (QVector() << QVector3D(0.0f, 0.0f, 1.0f) << QVector3D(0.0f, 0.0f, 1.0f) << QVector3D(0.0f, 0.0f, 1.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 0.0f) << QVector2D(1.0f, 0.0f) << QVector2D(0.0f, 1.0f)); const auto tangents = (QVector() << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f)); QTest::newRow("default_positiveZ_firstTriangle") << 1.0f << 1.0f << 1.0f << QSize(2,2) << QSize(2,2) << QSize(2,2) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 11; const auto indices = (QVector() << 22 << 21 << 23); const auto positions = (QVector() << QVector3D(0.5f, 0.5f, -0.5f) << QVector3D(-0.5f, -0.5f, -0.5f) << QVector3D(-0.5f, 0.5f, -0.5f)); const auto normals = (QVector() << QVector3D(0.0f, 0.0f, -1.0f) << QVector3D(0.0f, 0.0f, -1.0f) << QVector3D(0.0f, 0.0f, -1.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 1.0f) << QVector2D(1.0f, 0.0f) << QVector2D(1.0f, 1.0f)); const auto tangents = (QVector() << QVector4D(-1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(-1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(-1.0f, 0.0f, 0.0f, 1.0f)); QTest::newRow("default_negativeZ_lastTriangle") << 1.0f << 1.0f << 1.0f << QSize(2,2) << QSize(2,2) << QSize(2,2) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 0; const auto indices = (QVector() << 0 << 1 << 2); const auto positions = (QVector() << QVector3D(1.0f, -1.5f, -2.5f) << QVector3D(1.0f, 1.5f, -2.5f) << QVector3D(1.0f, -1.5f, -1.25f)); const auto normals = (QVector() << QVector3D(1.0f, 0.0f, 0.0f) << QVector3D(1.0f, 0.0f, 0.0f) << QVector3D(1.0f, 0.0f, 0.0f)); const auto texCoords = (QVector() << QVector2D(1.0f, 0.0f) << QVector2D(1.0f, 1.0f) << QVector2D(0.75f, 0.0f)); const auto tangents = (QVector() << QVector4D(0.0f, 0.0f, -1.0f, 1.0f) << QVector4D(0.0f, 0.0f, -1.0f, 1.0f) << QVector4D(0.0f, 0.0f, -1.0f, 1.0f)); QTest::newRow("default_positiveX_firstTriangle_nonSymmetric") << 2.0f << 3.0f << 5.0f << QSize(2,3) << QSize(2,5) << QSize(2,9) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 15; const auto indices = (QVector() << 18 << 17 << 19); const auto positions = (QVector() << QVector3D(-1.0f, -1.5f, -2.5f) << QVector3D(-1.0f, 1.5f, -1.25f) << QVector3D(-1.0f, 1.5f, -2.5f)); const auto normals = (QVector() << QVector3D(-1.0f, 0.0f, 0.0f) << QVector3D(-1.0f, 0.0f, 0.0f) << QVector3D(-1.0f, 0.0f, 0.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 0.0f) << QVector2D(0.25f, 1.0f) << QVector2D(0.0f, 1.0f)); const auto tangents = (QVector() << QVector4D(0.0f, 0.0f, 1.0f, 1.0f) << QVector4D(0.0f, 0.0f, 1.0f, 1.0f) << QVector4D(0.0f, 0.0f, 1.0f, 1.0f)); QTest::newRow("default_negativeX_lastTriangle_nonSymmetric") << 2.0f << 3.0f << 5.0f << QSize(2,3) << QSize(2,5) << QSize(2,9) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 16; const auto indices = (QVector() << 20 << 21 << 22); const auto positions = (QVector() << QVector3D(-1.0f, 1.5f, 2.5f) << QVector3D(1.0f, 1.5f, 2.5f) << QVector3D(-1.0f, 1.5f, 1.875f)); const auto normals = (QVector() << QVector3D(0.0f, 1.0f, 0.0f) << QVector3D(0.0f, 1.0f, 0.0f) << QVector3D(0.0f, 1.0f, 0.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 0.0f) << QVector2D(1.0f, 0.0f) << QVector2D(0.0f, 0.125f)); const auto tangents = (QVector() << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f)); QTest::newRow("default_positiveY_firstTriangle_nonSymmetric") << 2.0f << 3.0f << 5.0f << QSize(2,3) << QSize(2,5) << QSize(2,9) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 47; const auto indices = (QVector() << 54 << 53 << 55); const auto positions = (QVector() << QVector3D(-1.0f, -1.5f, 2.5f) << QVector3D(1.0f, -1.5f, 1.875f) << QVector3D(1.0f, -1.5f, 2.5f)); const auto normals = (QVector() << QVector3D(0.0f, -1.0f, 0.0f) << QVector3D(0.0f, -1.0f, 0.0f) << QVector3D(0.0f, -1.0f, 0.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 1.0f) << QVector2D(1.0f, 0.875f) << QVector2D(1.0f, 1.0f)); const auto tangents = (QVector() << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f)); QTest::newRow("default_negativeY_lastTriangle_nonSymmetric") << 2.0f << 3.0f << 5.0f << QSize(2,3) << QSize(2,5) << QSize(2,9) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 48; const auto indices = (QVector() << 56 << 57 << 58); const auto positions = (QVector() << QVector3D(-1.0f, -1.5f, 2.5f) << QVector3D(1.0f, -1.5f, 2.5f) << QVector3D(-1.0f, 0.0f, 2.5f)); const auto normals = (QVector() << QVector3D(0.0f, 0.0f, 1.0f) << QVector3D(0.0f, 0.0f, 1.0f) << QVector3D(0.0f, 0.0f, 1.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 0.0f) << QVector2D(1.0f, 0.0f) << QVector2D(0.0f, 0.5f)); const auto tangents = (QVector() << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(1.0f, 0.0f, 0.0f, 1.0f)); QTest::newRow("default_positiveZ_firstTriangle_nonSymmetric") << 2.0f << 3.0f << 5.0f << QSize(2,3) << QSize(2,5) << QSize(2,9) << triangleIndex << indices << positions << normals << texCoords << tangents; } { const int triangleIndex = 55; const auto indices = (QVector() << 66 << 65 << 67); const auto positions = (QVector() << QVector3D(1.0f, 1.5f, -2.5f) << QVector3D(-1.0f, 0.0f, -2.5f) << QVector3D(-1.0f, 1.5f, -2.5f)); const auto normals = (QVector() << QVector3D(0.0f, 0.0f, -1.0f) << QVector3D(0.0f, 0.0f, -1.0f) << QVector3D(0.0f, 0.0f, -1.0f)); const auto texCoords = (QVector() << QVector2D(0.0f, 1.0f) << QVector2D(1.0f, 0.5f) << QVector2D(1.0f, 1.0f)); const auto tangents = (QVector() << QVector4D(-1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(-1.0f, 0.0f, 0.0f, 1.0f) << QVector4D(-1.0f, 0.0f, 0.0f, 1.0f)); QTest::newRow("default_negativeZ_lastTriangle_nonSymmetric") << 2.0f << 3.0f << 5.0f << QSize(2,3) << QSize(2,5) << QSize(2,9) << triangleIndex << indices << positions << normals << texCoords << tangents; } } void generatedGeometryShouldBeConsistent() { // GIVEN Qt3DExtras::QCuboidGeometry geometry; const QVector 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(float, xExtent); QFETCH(float, yExtent); QFETCH(float, zExtent); QFETCH(QSize, xyMeshResolution); QFETCH(QSize, yzMeshResolution); QFETCH(QSize, xzMeshResolution); geometry.setXExtent(xExtent); geometry.setYExtent(yExtent); geometry.setZExtent(zExtent); geometry.setXYMeshResolution(xyMeshResolution); geometry.setYZMeshResolution(yzMeshResolution); geometry.setXZMeshResolution(xzMeshResolution); 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, indices); QFETCH(QVector, positions); QFETCH(QVector, normals); QFETCH(QVector, texCoords); QFETCH(QVector, tangents); int i = 0; for (auto index : indices) { const auto testIndex = extractIndexData(indexAttribute, 3 * triangleIndex + i); QCOMPARE(testIndex, indices.at(i)); const auto position = extractVertexData(positionAttribute, index); QCOMPARE(position, positions.at(i)); const auto normal = extractVertexData(normalAttribute, index); QCOMPARE(normal, normals.at(i)); const auto texCoord = extractVertexData(texCoordAttribute, index); QCOMPARE(texCoord, texCoords.at(i)); const auto tangent = extractVertexData(tangentAttribute, index); QCOMPARE(tangent, tangents.at(i)); ++i; } } }; QTEST_APPLESS_MAIN(tst_QCuboidGeometry) #include "tst_qcuboidgeometry.moc"