/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt3D module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** 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 Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** 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-2.0.html and ** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define VISUAL_CHECK 5000 // The value indicates the time for visual check in ms //#define PRESERVE_EXPORT // Uncomment to preserve export directory contents for analysis class tst_gltfPlugins : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void exportAndImport_data(); void exportAndImport(); private: void createTestScene(); Qt3DCore::QEntity *findCameraChild(Qt3DCore::QEntity *entity, Qt3DRender::QCameraLens::ProjectionType type); void walkEntity(Qt3DCore::QEntity *entity, int depth); void createAndAddEntity(const QString &name, Qt3DCore::QComponent *comp1 = nullptr, Qt3DCore::QComponent *comp2 = nullptr, Qt3DCore::QComponent *comp3 = nullptr, Qt3DCore::QEntity *parent = nullptr); void addPositionAttributeToGeometry(Qt3DCore::QGeometry *geometry, Qt3DCore::QBuffer *buffer, int count); void addIndexAttributeToGeometry(Qt3DCore::QGeometry *geometry, Qt3DCore::QBuffer *buffer, int count); void addColorAttributeToGeometry(Qt3DCore::QGeometry *geometry, Qt3DCore::QBuffer *buffer, int count); Qt3DCore::QEntity *findChildEntity(Qt3DCore::QEntity *entity, const QString &name); Qt3DCore::QTransform *transformComponent(Qt3DCore::QEntity *entity); Qt3DRender::QAbstractLight *lightComponent(Qt3DCore::QEntity *entity); Qt3DRender::QCameraLens *cameraComponent(Qt3DCore::QEntity *entity); Qt3DRender::QGeometryRenderer *meshComponent(Qt3DCore::QEntity *entity); Qt3DRender::QMaterial *materialComponent(Qt3DCore::QEntity *entity); void compareComponents(Qt3DCore::QComponent *c1, Qt3DCore::QComponent *c2); Qt3DCore::QAttribute *findAttribute(const QString &name, Qt3DCore::QAttribute::AttributeType type, Qt3DCore::QGeometry *geometry); void compareAttributes(Qt3DCore::QAttribute *a1, Qt3DCore::QAttribute *a2); void compareParameters(const QList ¶ms1, const QList ¶ms2); void compareRenderPasses(const QList &passes1, const QList &passes2); void compareFilterKeys(const QList &keys1, const QList &keys2); QUrl getTextureUrl(Qt3DRender::QAbstractTexture *tex); Qt3DRender::QGeometryRenderer *createCustomCube(); Qt3DRender::QEffect *createOnTopEffect(); QTemporaryDir *m_exportDir; #ifdef VISUAL_CHECK Qt3DExtras::Qt3DWindow *m_view1; Qt3DExtras::Qt3DWindow *m_view2; #endif Qt3DCore::QEntity *m_sceneRoot1; Qt3DCore::QEntity *m_sceneRoot2; QHash m_entityMap; }; void tst_gltfPlugins::initTestCase() { #ifndef VISUAL_CHECK // QEffect doesn't get registered unless aspects are initialized, generating warnings in // material comparisons. qRegisterMetaType(); #endif } void tst_gltfPlugins::init() { m_exportDir = new QTemporaryDir; #ifdef VISUAL_CHECK m_view1 = new Qt3DExtras::Qt3DWindow; m_view1->setTitle(QStringLiteral("Original scene")); m_view2 = new Qt3DExtras::Qt3DWindow; m_view2->setTitle(QStringLiteral("Imported scene")); #endif } void tst_gltfPlugins::cleanup() { delete m_sceneRoot1; delete m_sceneRoot2; m_entityMap.clear(); #ifdef VISUAL_CHECK delete m_view1; delete m_view2; #endif delete m_exportDir; // Make sure the slate is clean for the next case QCoreApplication::processEvents(); QTest::qWait(0); } void tst_gltfPlugins::walkEntity(Qt3DCore::QEntity *entity, int depth) { QString indent; indent.fill(' ', depth * 2); qDebug().noquote() << indent << "Entity:" << entity << "Components:" << entity->components(); for (auto child : entity->children()) { if (auto childEntity = qobject_cast(child)) walkEntity(childEntity, depth + 1); } } void tst_gltfPlugins::createAndAddEntity(const QString &name, Qt3DCore::QComponent *comp1, Qt3DCore::QComponent *comp2, Qt3DCore::QComponent *comp3, Qt3DCore::QEntity *parent) { Qt3DCore::QEntity *parentEntity = parent ? parent : m_sceneRoot1; Qt3DCore::QEntity *entity = new Qt3DCore::QEntity(parentEntity); entity->setObjectName(name); if (comp1) { entity->addComponent(comp1); comp1->setObjectName(comp1->metaObject()->className()); } if (comp2) { entity->addComponent(comp2); comp2->setObjectName(comp2->metaObject()->className()); } if (comp3) { entity->addComponent(comp3); comp3->setObjectName(comp3->metaObject()->className()); } m_entityMap.insert(name, entity); } void tst_gltfPlugins::createTestScene() { #ifdef VISUAL_CHECK m_view1->defaultFrameGraph()->setClearColor(Qt::lightGray); m_view2->defaultFrameGraph()->setClearColor(Qt::lightGray); #endif m_sceneRoot1 = new Qt3DCore::QEntity(); m_sceneRoot1->setObjectName(QStringLiteral("Scene root")); m_sceneRoot2 = new Qt3DCore::QEntity(); m_sceneRoot2->setObjectName(QStringLiteral("Imported scene parent")); // Perspective camera { Qt3DRender::QCamera *camera = new Qt3DRender::QCamera(m_sceneRoot1); camera->setProjectionType(Qt3DRender::QCameraLens::PerspectiveProjection); camera->setViewCenter(QVector3D(0.0f, 1.5f, 0.0f)); camera->setPosition(QVector3D(0.0f, 3.5f, 15.0f)); camera->setNearPlane(0.001f); camera->setFarPlane(10000.0f); camera->setObjectName(QStringLiteral("Main camera")); camera->transform()->setObjectName(QStringLiteral("Main camera transform")); camera->lens()->setObjectName(QStringLiteral("Main camera lens")); camera->setFieldOfView(30.0f); camera->setAspectRatio(1.0f); m_entityMap.insert(camera->objectName(), camera); } // Ortho camera { Qt3DCore::QEntity *camera = new Qt3DCore::QEntity(m_sceneRoot1); camera->setObjectName(QStringLiteral("Ortho camera")); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(0.0f, 0.0f, -15.0f)); transform->setObjectName(QStringLiteral("Ortho camera transform")); camera->addComponent(transform); Qt3DRender::QCameraLens *lens = new Qt3DRender::QCameraLens; lens->setProjectionType(Qt3DRender::QCameraLens::OrthographicProjection); lens->setNearPlane(0.001f); lens->setFarPlane(10000.0f); lens->setRight(7.0f); lens->setLeft(-7.0f); lens->setTop(5.0f); lens->setBottom(-5.0f); lens->setObjectName(QStringLiteral("Ortho camera lens")); camera->addComponent(lens); m_entityMap.insert(camera->objectName(), camera); #ifdef VISUAL_CHECK m_view1->defaultFrameGraph()->setCamera(camera); #endif } // Point light { Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight; light->setColor(QColor("#FFDDAA")); light->setIntensity(0.9f); light->setConstantAttenuation(0.03f); light->setLinearAttenuation(0.04f); light->setQuadraticAttenuation(0.01f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(-6.0f, 2.0f, 3.0f)); createAndAddEntity(QStringLiteral("Point Light"), light, transform); } // Directional light { Qt3DRender::QDirectionalLight *light = new Qt3DRender::QDirectionalLight; light->setColor(QColor("#BBCCEE")); light->setIntensity(0.75f); light->setWorldDirection(QVector3D(-1.0f, -1.0f, -1.0f)); createAndAddEntity(QStringLiteral("Directional Light"), light); } // Spot light { Qt3DRender::QSpotLight *light = new Qt3DRender::QSpotLight; light->setColor(QColor("#5599DD")); light->setIntensity(2.0f); light->setConstantAttenuation(0.03f); light->setLinearAttenuation(0.04f); light->setQuadraticAttenuation(0.01f); light->setLocalDirection(QVector3D(0.0f, -1.0f, -1.0f)); light->setCutOffAngle(30.0f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(0.0f, 5.0f, 0.0f)); createAndAddEntity(QStringLiteral("Spot Light"), light, transform); } // Cube with DiffuseMap { Qt3DExtras::QDiffuseMapMaterial *material = new Qt3DExtras::QDiffuseMapMaterial(); Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); material->diffuse()->addTextureImage(diffuseTextureImage); material->setAmbient(QColor("#000088")); material->setSpecular(QColor("#FFFF00")); material->setShininess(30.0); material->setTextureScale(2.0f); diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png"))); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setScale(0.75f); transform->setTranslation(QVector3D(2.0f, 1.0f, -1.0f)); Qt3DExtras::QCuboidMesh *mesh = new Qt3DExtras::QCuboidMesh; mesh->setXExtent(1.2f); mesh->setYExtent(1.1f); mesh->setZExtent(0.9f); mesh->setYZMeshResolution(QSize(2, 2)); mesh->setYZMeshResolution(QSize(2, 3)); mesh->setYZMeshResolution(QSize(3, 2)); Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Cube with DiffuseMap"), renderer, material, transform); } // Cone with PhongAlpha { Qt3DExtras::QPhongAlphaMaterial *material = new Qt3DExtras::QPhongAlphaMaterial(); material->setAlpha(0.6f); material->setAmbient(QColor("#550000")); material->setDiffuse(QColor("#00FFFF")); material->setSpecular(QColor("#FFFF00")); material->setShininess(20.0f); material->setSourceRgbArg(Qt3DRender::QBlendEquationArguments::Source1Color); material->setSourceAlphaArg(Qt3DRender::QBlendEquationArguments::Source1Alpha); material->setDestinationRgbArg(Qt3DRender::QBlendEquationArguments::DestinationColor); material->setDestinationAlphaArg(Qt3DRender::QBlendEquationArguments::DestinationAlpha); material->setBlendFunctionArg(Qt3DRender::QBlendEquation::ReverseSubtract); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(2.0f, 1.0f, 1.0f)); transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -20.0f)); Qt3DExtras::QConeMesh *mesh = new Qt3DExtras::QConeMesh; mesh->setRings(2); mesh->setSlices(16); mesh->setTopRadius(0.5f); mesh->setBottomRadius(1.5f); mesh->setLength(0.9f); auto renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Cone with PhongAlpha"), renderer, material, transform); } // Cylinder with Phong { Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial(); material->setAmbient(QColor("#220022")); material->setDiffuse(QColor("#6633AA")); material->setSpecular(QColor("#66AA33")); material->setShininess(50.0f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(0.0f, 1.0f, 1.0f)); transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -45.0f)); Qt3DExtras::QCylinderMesh *mesh = new Qt3DExtras::QCylinderMesh; mesh->setRadius(0.5f); mesh->setRings(3); mesh->setLength(1.2f); mesh->setSlices(16); auto renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Cylinder with Phong"), renderer, material, transform); } // Plane with DiffuseSpecularMap { Qt3DExtras::QDiffuseSpecularMapMaterial *material = new Qt3DExtras::QDiffuseSpecularMapMaterial(); Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); material->diffuse()->addTextureImage(diffuseTextureImage); diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png"))); Qt3DRender::QTextureImage *specularTextureImage = new Qt3DRender::QTextureImage(); material->specular()->addTextureImage(specularTextureImage); specularTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_specular.png"))); material->setAmbient(QColor("#0000FF")); material->setTextureScale(3.0f); material->setShininess(15.0f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(-1.0f, 1.0f, 1.0f)); transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 45.0f)); Qt3DExtras::QPlaneMesh *mesh = new Qt3DExtras::QPlaneMesh; mesh->setMeshResolution(QSize(3, 3)); mesh->setHeight(1.5f); mesh->setWidth(1.2f); auto renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Plane with DiffuseSpecularMap"), renderer, material, transform); } // Sphere with NormalDiffuseMap { Qt3DExtras::QNormalDiffuseMapMaterial *material = new Qt3DExtras::QNormalDiffuseMapMaterial(); Qt3DRender::QTextureImage *normalTextureImage = new Qt3DRender::QTextureImage(); material->normal()->addTextureImage(normalTextureImage); normalTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_normal.png"))); Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); material->diffuse()->addTextureImage(diffuseTextureImage); diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png"))); material->setAmbient(QColor("#000044")); material->setSpecular(QColor("#0000CC")); material->setShininess(9.0f); material->setTextureScale(4.0f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(0.0f, 1.0f, -10.0f)); Qt3DExtras::QSphereMesh *mesh = new Qt3DExtras::QSphereMesh; mesh->setRadius(2.0f); mesh->setRings(16); mesh->setSlices(16); mesh->setGenerateTangents(true); auto renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseMap"), renderer, material, transform); } // Sphere with NormalDiffuseMapAlpha { Qt3DExtras::QNormalDiffuseMapAlphaMaterial *material = new Qt3DExtras::QNormalDiffuseMapAlphaMaterial(); Qt3DRender::QTextureImage *normalTextureImage = new Qt3DRender::QTextureImage(); material->normal()->addTextureImage(normalTextureImage); normalTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_normal.png"))); Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); material->diffuse()->addTextureImage(diffuseTextureImage); diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_with_alpha.png"))); material->setAmbient(QColor("#000044")); material->setSpecular(QColor("#0000CC")); material->setShininess(9.0f); material->setTextureScale(4.0f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(4.0f, 1.0f, -10.0f)); Qt3DExtras::QSphereMesh *mesh = new Qt3DExtras::QSphereMesh; mesh->setRadius(2.0f); mesh->setRings(16); mesh->setSlices(16); mesh->setGenerateTangents(true); auto renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseMapAlpha"), renderer, material, transform); } // Sphere with NormalDiffuseSpecularMap { Qt3DExtras::QNormalDiffuseSpecularMapMaterial *material = new Qt3DExtras::QNormalDiffuseSpecularMapMaterial(); Qt3DRender::QTextureImage *normalTextureImage = new Qt3DRender::QTextureImage(); material->normal()->addTextureImage(normalTextureImage); normalTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_normal.png"))); Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); material->diffuse()->addTextureImage(diffuseTextureImage); diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png"))); Qt3DRender::QTextureImage *specularTextureImage = new Qt3DRender::QTextureImage(); material->specular()->addTextureImage(specularTextureImage); specularTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_specular.png"))); material->setAmbient(QColor("#000044")); material->setShininess(9.0f); material->setTextureScale(4.0f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(-4.0f, 1.0f, -10.0f)); Qt3DExtras::QSphereMesh *mesh = new Qt3DExtras::QSphereMesh; mesh->setRadius(2.0f); mesh->setRings(16); mesh->setSlices(16); mesh->setGenerateTangents(true); auto renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseSpecularMap"), renderer, material, transform); } // Torus with Gooch { Qt3DExtras::QGoochMaterial *material = new Qt3DExtras::QGoochMaterial(); material->setDiffuse(QColor("#333333")); material->setSpecular(QColor("#550055")); material->setCool(QColor("#0055AA")); material->setWarm(QColor("#FF3300")); material->setAlpha(0.2f); material->setBeta(0.4f); material->setShininess(22.0f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(0.0f, 4.0f, -10.0f)); Qt3DExtras::QTorusMesh *mesh = new Qt3DExtras::QTorusMesh; mesh->setRadius(1.0f); mesh->setMinorRadius(0.5f); mesh->setRings(16); mesh->setSlices(16); auto renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Torus with Gooch"), renderer, material, transform); } // Custom cube with per-vertex colors { Qt3DExtras::QPerVertexColorMaterial *material = new Qt3DExtras::QPerVertexColorMaterial(); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(4.0f, 3.0f, -15.0f)); transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(1.0f, 1.0f, 1.0f, 270.0f)); Qt3DRender::QGeometryRenderer *boxMesh = createCustomCube(); Qt3DCore::QBuffer *colorDataBuffer = new Qt3DCore::QBuffer(boxMesh->view()->geometry()); QByteArray colorBufferData; colorBufferData.resize(8 * 4 * sizeof(float)); float *cPtr = reinterpret_cast(colorBufferData.data()); for (int i = 0; i < 8; i++) { cPtr[i * 4] = float(i) / 8.0f; cPtr[i * 4 + 1] = float(8 - i) / 8.0f; cPtr[i * 4 + 2] = float((i + 4) % 8) / 8.0f; cPtr[i * 4 + 3] = 1.0f; } colorDataBuffer->setData(colorBufferData); addColorAttributeToGeometry(boxMesh->view()->geometry(), colorDataBuffer, 8); createAndAddEntity(QStringLiteral("Custom cube with per-vertex colors"), boxMesh, material, transform); } // Child cylinder with Phong { Qt3DCore::QEntity *parentEntity = findChildEntity(m_sceneRoot1, QStringLiteral("Cylinder with Phong")); Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial(); material->setAmbient(QColor("#333333")); material->setDiffuse(QColor("#88FF00")); material->setSpecular(QColor("#000088")); material->setShininess(150.0f); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(0.0f, 4.0f, 0.0f)); Qt3DExtras::QCylinderMesh *mesh = new Qt3DExtras::QCylinderMesh; mesh->setRadius(0.25f); mesh->setRings(3); mesh->setLength(1.5f); mesh->setSlices(16); auto renderer = new Qt3DRender::QGeometryRenderer; renderer->setView(mesh); createAndAddEntity(QStringLiteral("Child with Phong"), renderer, material, transform, parentEntity); } // Cube with custom material { Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial; material->setEffect(createOnTopEffect()); material->addParameter(new Qt3DRender::QParameter(QStringLiteral("globalOffset"), QVector3D(-3.0f, 0.0f, 3.0f))); material->addParameter(new Qt3DRender::QParameter(QStringLiteral("extraYOffset"), 3)); material->effect()->addParameter(new Qt3DRender::QParameter(QStringLiteral("handleColor"), QColor(Qt::magenta))); material->effect()->addParameter(new Qt3DRender::QParameter(QStringLiteral("reverseOffset"), QVariant::fromValue(true))); Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; transform->setTranslation(QVector3D(0.0f, 2.0f, -40.0f)); transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(1.0f, 2.0f, 3.0f, 90.0f)); Qt3DRender::QGeometryRenderer *boxMesh = createCustomCube(); Qt3DCore::QBuffer *offsetBuffer = new Qt3DCore::QBuffer(boxMesh->view()->geometry()); QByteArray offsetBufferData; offsetBufferData.resize(8 * 3 * sizeof(float)); float *oPtr = reinterpret_cast(offsetBufferData.data()); for (int i = 0; i < 8; i++) { oPtr[i * 3] = float(i) / 4.0f; oPtr[i * 3 + 1] = float(8 - i) / 4.0f + 2.0f; oPtr[i * 3 + 2] = float((i + 4) % 8) / 4.0f; } offsetBuffer->setData(offsetBufferData); Qt3DCore::QAttribute *customAttribute = new Qt3DCore::QAttribute(); customAttribute->setAttributeType(Qt3DCore::QAttribute::VertexAttribute); customAttribute->setBuffer(offsetBuffer); customAttribute->setVertexBaseType(Qt3DCore::QAttribute::Float); customAttribute->setVertexSize(3); customAttribute->setByteOffset(0); customAttribute->setByteStride(0); customAttribute->setCount(8); customAttribute->setName(QStringLiteral("vertexOffset")); boxMesh->view()->geometry()->addAttribute(customAttribute); createAndAddEntity(QStringLiteral("Custom cube with on-top material"), boxMesh, material, transform); } #ifdef VISUAL_CHECK m_view1->setGeometry(30, 30, 400, 400); m_view1->setRootEntity(m_sceneRoot1); m_view1->show(); m_view2->setGeometry(450, 30, 400, 400); m_view2->setRootEntity(m_sceneRoot2); m_view2->show(); QTest::qWaitForWindowExposed(m_view1); QTest::qWaitForWindowExposed(m_view2); #endif } void tst_gltfPlugins::addPositionAttributeToGeometry(Qt3DCore::QGeometry *geometry, Qt3DCore::QBuffer *buffer, int count) { Qt3DCore::QAttribute *posAttribute = new Qt3DCore::QAttribute(); posAttribute->setAttributeType(Qt3DCore::QAttribute::VertexAttribute); posAttribute->setBuffer(buffer); posAttribute->setVertexBaseType(Qt3DCore::QAttribute::Float); posAttribute->setVertexSize(3); posAttribute->setByteOffset(0); posAttribute->setByteStride(0); posAttribute->setCount(count); posAttribute->setName(Qt3DCore::QAttribute::defaultPositionAttributeName()); geometry->addAttribute(posAttribute); } void tst_gltfPlugins::addIndexAttributeToGeometry(Qt3DCore::QGeometry *geometry, Qt3DCore::QBuffer *buffer, int count) { Qt3DCore::QAttribute *indexAttribute = new Qt3DCore::QAttribute(); indexAttribute->setAttributeType(Qt3DCore::QAttribute::IndexAttribute); indexAttribute->setBuffer(buffer); indexAttribute->setVertexBaseType(Qt3DCore::QAttribute::UnsignedShort); indexAttribute->setVertexSize(1); indexAttribute->setByteOffset(0); indexAttribute->setByteStride(0); indexAttribute->setCount(count); geometry->addAttribute(indexAttribute); } void tst_gltfPlugins::addColorAttributeToGeometry(Qt3DCore::QGeometry *geometry, Qt3DCore::QBuffer *buffer, int count) { Qt3DCore::QAttribute *colorAttribute = new Qt3DCore::QAttribute(); colorAttribute->setAttributeType(Qt3DCore::QAttribute::VertexAttribute); colorAttribute->setBuffer(buffer); colorAttribute->setVertexBaseType(Qt3DCore::QAttribute::Float); colorAttribute->setVertexSize(4); colorAttribute->setByteOffset(0); colorAttribute->setByteStride(0); colorAttribute->setCount(count); colorAttribute->setName(Qt3DCore::QAttribute::defaultColorAttributeName()); geometry->addAttribute(colorAttribute); } Qt3DCore::QEntity *tst_gltfPlugins::findChildEntity(Qt3DCore::QEntity *entity, const QString &name) { for (auto child : entity->children()) { if (auto childEntity = qobject_cast(child)) { if (childEntity->objectName() == name) return childEntity; if (auto foundEntity = findChildEntity(childEntity, name)) return foundEntity; } } return nullptr; } Qt3DCore::QTransform *tst_gltfPlugins::transformComponent(Qt3DCore::QEntity *entity) { for (auto component : entity->components()) { if (auto castedComponent = qobject_cast(component)) return castedComponent; } return nullptr; } Qt3DRender::QAbstractLight *tst_gltfPlugins::lightComponent(Qt3DCore::QEntity *entity) { for (auto component : entity->components()) { if (auto castedComponent = qobject_cast(component)) return castedComponent; } return nullptr; } Qt3DRender::QCameraLens *tst_gltfPlugins::cameraComponent(Qt3DCore::QEntity *entity) { for (auto component : entity->components()) { if (auto castedComponent = qobject_cast(component)) return castedComponent; } return nullptr; } Qt3DRender::QGeometryRenderer *tst_gltfPlugins::meshComponent(Qt3DCore::QEntity *entity) { for (auto component : entity->components()) { if (auto castedComponent = qobject_cast(component)) return castedComponent; } return nullptr; } Qt3DRender::QMaterial *tst_gltfPlugins::materialComponent(Qt3DCore::QEntity *entity) { for (auto component : entity->components()) { if (auto castedComponent = qobject_cast(component)) return castedComponent; } return nullptr; } void tst_gltfPlugins::compareComponents(Qt3DCore::QComponent *c1, Qt3DCore::QComponent *c2) { // Make sure component classes are the same and the non-pointer properties are the same QCOMPARE((c1 == nullptr), (c2 == nullptr)); if (c1) { // Transform names are lost in export, as the transform is just part of the node item if (!qobject_cast(c1)) QCOMPARE(c1->objectName(), c2->objectName()); QCOMPARE(c1->metaObject()->className(), c2->metaObject()->className()); // Meshes are all imported as generic meshes if (auto mesh1 = qobject_cast(c1)) { auto mesh2 = qobject_cast(c2); QVERIFY(mesh2 != nullptr); auto geometry1 = mesh1->view()->geometry(); auto geometry2 = mesh2->view()->geometry(); // Check that attributes match. compareAttributes( findAttribute(Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry1), findAttribute(Qt3DCore::QAttribute::defaultPositionAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry2)); compareAttributes( findAttribute(Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry1), findAttribute(Qt3DCore::QAttribute::defaultNormalAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry2)); compareAttributes( findAttribute(Qt3DCore::QAttribute::defaultTangentAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry1), findAttribute(Qt3DCore::QAttribute::defaultTangentAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry2)); compareAttributes( findAttribute(Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry1), findAttribute(Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry2)); compareAttributes( findAttribute(Qt3DCore::QAttribute::defaultColorAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry1), findAttribute(Qt3DCore::QAttribute::defaultColorAttributeName(), Qt3DCore::QAttribute::VertexAttribute, geometry2)); compareAttributes( findAttribute(QStringLiteral(""), Qt3DCore::QAttribute::IndexAttribute, geometry1), findAttribute(QStringLiteral(""), Qt3DCore::QAttribute::IndexAttribute, geometry2)); } else { int count = c1->metaObject()->propertyCount(); for (int i = 0; i < count; i++) { auto property = c1->metaObject()->property(i); auto v1 = c1->property(property.name()); auto v2 = c2->property(property.name()); if (v1.type() == QVariant::Bool) { QCOMPARE(v1.toBool(), v2.toBool()); } else if (v1.type() == QVariant::Color) { QCOMPARE(v1.value(), v2.value()); } else if (v1.type() == QVariant::Vector3D) { QCOMPARE(v1.value(), v2.value()); } else if (v1.type() == QVariant::Matrix4x4) { QCOMPARE(v1.value(), v2.value()); } else if (v1.canConvert(QMetaType::Float)) { QVERIFY(qFuzzyCompare(v1.toFloat(), v2.toFloat())); } } if (QString::fromLatin1(c1->metaObject()->className()) .endsWith(QStringLiteral("Qt3DRender::QMaterial"))) { auto m1 = qobject_cast(c1); auto m2 = qobject_cast(c2); QVERIFY(m1); QVERIFY(m2); auto e1 = m1->effect(); auto e2 = m2->effect(); QVERIFY(e1); QVERIFY(e2); QCOMPARE(e1->objectName(), e2->objectName()); QCOMPARE(e1->techniques().size(), e2->techniques().size()); compareParameters(m1->parameters(), m2->parameters()); compareParameters(e1->parameters(), e2->parameters()); for (auto t1 : e1->techniques()) { bool techMatch = false; for (auto t2 : e2->techniques()) { if (t1->objectName() == t2->objectName()) { techMatch = true; compareParameters(t1->parameters(), t2->parameters()); compareFilterKeys(t1->filterKeys(), t2->filterKeys()); compareRenderPasses(t1->renderPasses(), t2->renderPasses()); QCOMPARE(t1->graphicsApiFilter()->api(), t2->graphicsApiFilter()->api()); QCOMPARE(t1->graphicsApiFilter()->profile(), t2->graphicsApiFilter()->profile()); QCOMPARE(t1->graphicsApiFilter()->minorVersion(), t2->graphicsApiFilter()->minorVersion()); QCOMPARE(t1->graphicsApiFilter()->majorVersion(), t2->graphicsApiFilter()->majorVersion()); QCOMPARE(t1->graphicsApiFilter()->extensions(), t2->graphicsApiFilter()->extensions()); QCOMPARE(t1->graphicsApiFilter()->vendor(), t2->graphicsApiFilter()->vendor()); } } QVERIFY(techMatch); } } } } } Qt3DCore::QAttribute *tst_gltfPlugins::findAttribute(const QString &name, Qt3DCore::QAttribute::AttributeType type, Qt3DCore::QGeometry *geometry) { for (auto att : geometry->attributes()) { if ((type == Qt3DCore::QAttribute::IndexAttribute && type == att->attributeType()) || name == att->name()) { return att; } } return nullptr; } void tst_gltfPlugins::compareAttributes(Qt3DCore::QAttribute *a1, Qt3DCore::QAttribute *a2) { QCOMPARE(a1 == nullptr, a2 == nullptr); if (a1) { QCOMPARE(a1->attributeType(), a2->attributeType()); QCOMPARE(a1->vertexBaseType(), a2->vertexBaseType()); QCOMPARE(a1->vertexSize(), a2->vertexSize()); QCOMPARE(a1->count(), a2->count()); } } void tst_gltfPlugins::compareParameters(const QList ¶ms1, const QList ¶ms2) { QCOMPARE(params1.size(), params2.size()); for (auto p1 : params1) { bool pMatch = false; for (auto p2 : params2) { if (p1->name() == p2->name()) { pMatch = true; if (p1->value().type() == QVariant::Color) { // Colors are imported as QVector4Ds QColor color = p1->value().value(); QVector4D vec = p2->value().value(); QCOMPARE(color.redF(), vec.x()); QCOMPARE(color.greenF(), vec.y()); QCOMPARE(color.blueF(), vec.z()); QCOMPARE(color.alphaF(), vec.w()); } else if (p1->value().canConvert()) { QUrl u1 = getTextureUrl(p1->value().value()); QUrl u2 = getTextureUrl(p2->value().value()); QCOMPARE(u1.fileName(), u2.fileName()); } else { QCOMPARE(p1->value(), p2->value()); } } } QVERIFY(pMatch); } } void tst_gltfPlugins::compareRenderPasses(const QList &passes1, const QList &passes2) { QCOMPARE(passes1.size(), passes2.size()); for (auto pass1 : passes1) { bool passMatch = false; for (auto pass2 : passes2) { if (pass1->objectName() == pass2->objectName()) { passMatch = true; compareFilterKeys(pass1->filterKeys(), pass2->filterKeys()); compareParameters(pass1->parameters(), pass2->parameters()); const QList states1 = pass1->renderStates(); const QList states2 = pass2->renderStates(); QCOMPARE(states1.size(), states2.size()); for (auto state1 : states1) { bool stateMatch = false; for (auto state2 : states2) { if (state1->metaObject()->className() == state2->metaObject()->className()) { stateMatch = true; } } QVERIFY(stateMatch); } QCOMPARE(pass1->shaderProgram()->vertexShaderCode(), pass2->shaderProgram()->vertexShaderCode()); QCOMPARE(pass1->shaderProgram()->fragmentShaderCode(), pass2->shaderProgram()->fragmentShaderCode()); } } QVERIFY(passMatch); } } void tst_gltfPlugins::compareFilterKeys(const QList &keys1, const QList &keys2) { QCOMPARE(keys1.size(), keys2.size()); for (auto k1 : keys1) { bool kMatch = false; for (auto k2 : keys2) { if (k1->name() == k2->name()) { kMatch = true; QCOMPARE(k1->value(), k2->value()); } } QVERIFY(kMatch); } } QUrl tst_gltfPlugins::getTextureUrl(Qt3DRender::QAbstractTexture *tex) { QUrl url; if (tex->textureImages().size()) { Qt3DRender::QTextureImage *img = qobject_cast( tex->textureImages().at(0)); if (img) url = img->source(); } return url; } Qt3DRender::QGeometryRenderer *tst_gltfPlugins::createCustomCube() { Qt3DCore::QGeometryView *boxView = new Qt3DCore::QGeometryView(); Qt3DCore::QGeometry *boxGeometry = new Qt3DCore::QGeometry(boxView); Qt3DCore::QBuffer *boxDataBuffer = new Qt3DCore::QBuffer(boxGeometry); Qt3DCore::QBuffer *indexDataBuffer = new Qt3DCore::QBuffer(boxGeometry); QByteArray vertexBufferData; QByteArray indexBufferData; vertexBufferData.resize(8 * 3 * sizeof(float)); indexBufferData.resize(12 * 3 * sizeof(ushort)); float dimension = 1.0f; float *vPtr = reinterpret_cast(vertexBufferData.data()); vPtr[0] = -dimension; vPtr[1] = -dimension; vPtr[2] = -dimension; vPtr[3] = dimension; vPtr[4] = -dimension; vPtr[5] = -dimension; vPtr[6] = dimension; vPtr[7] = -dimension; vPtr[8] = dimension; vPtr[9] = -dimension; vPtr[10] = -dimension; vPtr[11] = dimension; vPtr[12] = -dimension; vPtr[13] = dimension; vPtr[14] = -dimension; vPtr[15] = dimension; vPtr[16] = dimension; vPtr[17] = -dimension; vPtr[18] = dimension; vPtr[19] = dimension; vPtr[20] = dimension; vPtr[21] = -dimension; vPtr[22] = dimension; vPtr[23] = dimension; ushort *iPtr = reinterpret_cast(indexBufferData.data()); iPtr[0] = 2; iPtr[1] = 0; iPtr[2] = 1; iPtr[3] = 2; iPtr[4] = 3; iPtr[5] = 0; iPtr[6] = 1; iPtr[7] = 6; iPtr[8] = 2; iPtr[9] = 1; iPtr[10] = 5; iPtr[11] = 6; iPtr[12] = 2; iPtr[13] = 7; iPtr[14] = 3; iPtr[15] = 2; iPtr[16] = 6; iPtr[17] = 7; iPtr[18] = 6; iPtr[19] = 5; iPtr[20] = 4; iPtr[21] = 6; iPtr[22] = 4; iPtr[23] = 7; iPtr[24] = 7; iPtr[25] = 0; iPtr[26] = 3; iPtr[27] = 7; iPtr[28] = 4; iPtr[29] = 0; iPtr[30] = 4; iPtr[31] = 1; iPtr[32] = 0; iPtr[33] = 4; iPtr[34] = 5; iPtr[35] = 1; boxDataBuffer->setData(vertexBufferData); indexDataBuffer->setData(indexBufferData); addPositionAttributeToGeometry(boxGeometry, boxDataBuffer, 8); addIndexAttributeToGeometry(boxGeometry, indexDataBuffer, 36); boxView->setInstanceCount(1); boxView->setIndexOffset(0); boxView->setFirstInstance(0); boxView->setVertexCount(36); boxView->setPrimitiveType(Qt3DCore::QGeometryView::Triangles); boxView->setGeometry(boxGeometry); Qt3DRender::QGeometryRenderer *boxMesh = new Qt3DRender::QGeometryRenderer; boxMesh->setView(boxView); return boxMesh; } Qt3DRender::QEffect *tst_gltfPlugins::createOnTopEffect() { Qt3DRender::QEffect *effect = new Qt3DRender::QEffect; Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique(); technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile); technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL); technique->graphicsApiFilter()->setMajorVersion(2); technique->graphicsApiFilter()->setMinorVersion(1); Qt3DRender::QTechnique *techniqueCore = new Qt3DRender::QTechnique(); techniqueCore->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile); techniqueCore->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL); techniqueCore->graphicsApiFilter()->setMajorVersion(3); techniqueCore->graphicsApiFilter()->setMinorVersion(1); Qt3DRender::QTechnique *techniqueES2 = new Qt3DRender::QTechnique(); techniqueES2->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES); techniqueES2->graphicsApiFilter()->setMajorVersion(2); techniqueES2->graphicsApiFilter()->setMinorVersion(0); techniqueES2->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile); Qt3DRender::QFilterKey *filterkey1 = new Qt3DRender::QFilterKey(effect); Qt3DRender::QFilterKey *filterkey2 = new Qt3DRender::QFilterKey(); filterkey1->setName(QStringLiteral("renderingStyle")); filterkey1->setValue(QStringLiteral("forward")); filterkey2->setName(QStringLiteral("dummyKey")); filterkey2->setValue(QStringLiteral("dummyValue")); Qt3DRender::QParameter *parameter1 = new Qt3DRender::QParameter(QStringLiteral("handleColor"), QColor(Qt::yellow)); Qt3DRender::QParameter *parameter2 = new Qt3DRender::QParameter(QStringLiteral("customAlpha"), 1.0f); Qt3DRender::QParameter *parameter3 = new Qt3DRender::QParameter(QStringLiteral("handleColor"), QColor(Qt::blue)); Qt3DRender::QTexture2D *texture = new Qt3DRender::QTexture2D; Qt3DRender::QParameter *parameter4 = new Qt3DRender::QParameter(QStringLiteral("customTexture"), texture); Qt3DRender::QTextureImage *ti = new Qt3DRender::QTextureImage(); parameter4->value().value()->addTextureImage(ti); ti->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png"))); technique->addFilterKey(filterkey1); technique->addFilterKey(filterkey2); techniqueES2->addFilterKey(filterkey1); techniqueES2->addFilterKey(filterkey2); techniqueCore->addFilterKey(filterkey1); technique->addParameter(parameter1); technique->addParameter(parameter2); technique->addParameter(parameter4); techniqueES2->addParameter(parameter1); techniqueES2->addParameter(parameter2); Qt3DRender::QShaderProgram *shader = new Qt3DRender::QShaderProgram(); Qt3DRender::QShaderProgram *shaderES2 = new Qt3DRender::QShaderProgram(); shader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource( QUrl(QStringLiteral("qrc:/ontopmaterial.vert")))); shader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource( QUrl(QStringLiteral("qrc:/ontopmaterial.frag")))); shaderES2->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource( QUrl(QStringLiteral("qrc:/ontopmaterialES2.vert")))); shaderES2->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource( QUrl(QStringLiteral("qrc:/ontopmaterialES2.frag")))); shader->setObjectName(QStringLiteral("Basic shader")); shaderES2->setObjectName(QStringLiteral("ES2 shader")); Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass(); Qt3DRender::QRenderPass *renderPassES2 = new Qt3DRender::QRenderPass(); renderPass->setShaderProgram(shader); renderPassES2->setShaderProgram(shaderES2); renderPass->addFilterKey(filterkey2); renderPass->addParameter(parameter3); Qt3DRender::QColorMask *cmask = new Qt3DRender::QColorMask; cmask->setRedMasked(false); renderPass->addRenderState(cmask); Qt3DRender::QBlendEquation *be = new Qt3DRender::QBlendEquation; be->setBlendFunction(Qt3DRender::QBlendEquation::Subtract); renderPass->addRenderState(be); technique->addRenderPass(renderPassES2); techniqueES2->addRenderPass(renderPassES2); techniqueCore->addRenderPass(renderPass); technique->setObjectName(QStringLiteral("Basic technique")); techniqueES2->setObjectName(QStringLiteral("ES2 technique")); techniqueCore->setObjectName(QStringLiteral("Core technique")); renderPass->setObjectName(QStringLiteral("Basic pass")); renderPassES2->setObjectName(QStringLiteral("ES2 pass")); effect->addTechnique(technique); effect->addTechnique(techniqueES2); effect->addTechnique(techniqueCore); effect->setObjectName(QStringLiteral("OnTopEffect")); return effect; } Qt3DCore::QEntity *tst_gltfPlugins::findCameraChild(Qt3DCore::QEntity *entity, Qt3DRender::QCameraLens::ProjectionType type) { for (auto child : entity->children()) { if (auto childEntity = qobject_cast(child)) { for (auto component : childEntity->components()) { if (auto cameraLens = qobject_cast(component)) { if (cameraLens->projectionType() == type) return childEntity; } } if (auto cameraEntity = findCameraChild(childEntity, type)) return cameraEntity; } } return nullptr; } void tst_gltfPlugins::exportAndImport_data() { QTest::addColumn("compactJson"); QTest::newRow("No options") << false; #ifndef VISUAL_CHECK QTest::newRow("Compact json") << true; #endif } void tst_gltfPlugins::exportAndImport() { QFETCH(bool, compactJson); createTestScene(); #ifdef PRESERVE_EXPORT m_exportDir->setAutoRemove(false); qDebug() << "Export Directory:" << m_exportDir->path(); #endif const QString sceneName = QStringLiteral("MyGLTFScene"); const QString exportDir = m_exportDir->path(); // Export the created scene using GLTF export plugin QStringList keys = Qt3DRender::QSceneExportFactory::keys(); for (const QString &key : keys) { Qt3DRender::QSceneExporter *exporter = Qt3DRender::QSceneExportFactory::create(key, QStringList()); if (exporter != nullptr && key == QStringLiteral("gltfexport")) { QVariantHash options; options.insert(QStringLiteral("compactJson"), QVariant(compactJson)); exporter->exportScene(m_sceneRoot1, exportDir, sceneName, options); break; } } QCoreApplication::processEvents(); // Import the exported scene using GLTF import plugin Qt3DCore::QEntity *importedScene = nullptr; keys = Qt3DRender::QSceneImportFactory::keys(); for (auto key : keys) { Qt3DRender::QSceneImporter *importer = Qt3DRender::QSceneImportFactory::create(key, QStringList()); if (importer != nullptr && key == QStringLiteral("gltf")) { QString sceneSource = exportDir; if (!sceneSource.endsWith(QLatin1Char('/'))) sceneSource.append(QLatin1Char('/')); sceneSource += sceneName; sceneSource += QLatin1Char('/'); sceneSource += sceneName; sceneSource += QStringLiteral(".qgltf"); importer->setSource(QUrl::fromLocalFile(sceneSource)); importedScene = importer->scene(); break; } } importedScene->setParent(m_sceneRoot2); // Compare contents of the original scene and the exported one. for (auto it = m_entityMap.begin(), end = m_entityMap.end(); it != end; ++it) { QString name = it.key(); Qt3DCore::QEntity *exportedEntity = it.value(); Qt3DCore::QEntity *importedEntity = findChildEntity(importedScene, name); QVERIFY(importedEntity != nullptr); if (importedEntity) { compareComponents(transformComponent(exportedEntity), transformComponent(importedEntity)); compareComponents(lightComponent(exportedEntity), lightComponent(importedEntity)); compareComponents(cameraComponent(exportedEntity), cameraComponent(importedEntity)); compareComponents(meshComponent(exportedEntity), meshComponent(importedEntity)); compareComponents(materialComponent(exportedEntity), materialComponent(importedEntity)); Qt3DRender::QCamera *exportedCamera = qobject_cast(exportedEntity); if (exportedCamera) { Qt3DRender::QCamera *importedCamera = qobject_cast(importedEntity); QVERIFY(importedCamera != nullptr); QCOMPARE(exportedCamera->position(), importedCamera->position()); QCOMPARE(exportedCamera->upVector(), importedCamera->upVector()); QCOMPARE(exportedCamera->viewCenter(), importedCamera->viewCenter()); } } } #ifdef VISUAL_CHECK qDebug() << "Dumping original entity tree:"; walkEntity(m_sceneRoot1, 0); qDebug() << "Dumping imported entity tree:"; walkEntity(importedScene, 0); // Find the camera to actually show the scene m_view2->defaultFrameGraph()->setCamera( findCameraChild(m_sceneRoot2, Qt3DRender::QCameraLens::OrthographicProjection)); QTest::qWait(VISUAL_CHECK); m_view1->defaultFrameGraph()->setCamera( findCameraChild(m_sceneRoot1, Qt3DRender::QCameraLens::PerspectiveProjection)); m_view2->defaultFrameGraph()->setCamera( findCameraChild(m_sceneRoot2, Qt3DRender::QCameraLens::PerspectiveProjection)); QTest::qWait(VISUAL_CHECK); #endif } QTEST_MAIN(tst_gltfPlugins) #include "tst_gltfplugins.moc"