| /**************************************************************************** |
| ** |
| ** 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 <QtTest/qtest.h> |
| #include <QtCore/qtemporarydir.h> |
| #include <QtGui/qimage.h> |
| |
| #include <private/qsceneimporter_p.h> |
| #include <private/qsceneexportfactory_p.h> |
| #include <private/qsceneexporter_p.h> |
| #include <private/qsceneimportfactory_p.h> |
| #include <private/qsceneimporter_p.h> |
| |
| #include <Qt3DCore/qentity.h> |
| #include <Qt3DCore/qtransform.h> |
| |
| #include <Qt3DRender/qcamera.h> |
| #include <Qt3DRender/qcameralens.h> |
| #include <Qt3DRender/qtextureimage.h> |
| #include <Qt3DRender/qspotlight.h> |
| #include <Qt3DRender/qdirectionallight.h> |
| #include <Qt3DRender/qpointlight.h> |
| #include <Qt3DRender/qattribute.h> |
| #include <Qt3DRender/qbuffer.h> |
| #include <Qt3DRender/qeffect.h> |
| #include <Qt3DRender/qshaderprogram.h> |
| #include <Qt3DRender/qtechnique.h> |
| #include <Qt3DRender/qparameter.h> |
| #include <Qt3DRender/qgraphicsapifilter.h> |
| #include <Qt3DRender/qfilterkey.h> |
| #include <Qt3DRender/qtexture.h> |
| #include <Qt3DRender/qcolormask.h> |
| #include <Qt3DRender/qblendequation.h> |
| |
| #include <Qt3DExtras/qconemesh.h> |
| #include <Qt3DExtras/qcuboidmesh.h> |
| #include <Qt3DExtras/qcylindermesh.h> |
| #include <Qt3DExtras/qplanemesh.h> |
| #include <Qt3DExtras/qspheremesh.h> |
| #include <Qt3DExtras/qtorusmesh.h> |
| #include <Qt3DExtras/qt3dwindow.h> |
| #include <Qt3DExtras/qphongmaterial.h> |
| #include <Qt3DExtras/qphongalphamaterial.h> |
| #include <Qt3DExtras/qdiffusemapmaterial.h> |
| #include <Qt3DExtras/qdiffusespecularmapmaterial.h> |
| #include <Qt3DExtras/qnormaldiffusemapmaterial.h> |
| #include <Qt3DExtras/qnormaldiffusemapalphamaterial.h> |
| #include <Qt3DExtras/qnormaldiffusespecularmapmaterial.h> |
| #include <Qt3DExtras/qgoochmaterial.h> |
| #include <Qt3DExtras/qpervertexcolormaterial.h> |
| #include <Qt3DExtras/qforwardrenderer.h> |
| |
| //#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(Qt3DRender::QGeometry *geometry, |
| Qt3DRender::QBuffer *buffer, int count); |
| void addIndexAttributeToGeometry(Qt3DRender::QGeometry *geometry, |
| Qt3DRender::QBuffer *buffer, int count); |
| void addColorAttributeToGeometry(Qt3DRender::QGeometry *geometry, |
| Qt3DRender::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); |
| Qt3DRender::QAttribute *findAttribute(const QString &name, |
| Qt3DRender::QAttribute::AttributeType type, |
| Qt3DRender::QGeometry *geometry); |
| void compareAttributes(Qt3DRender::QAttribute *a1, Qt3DRender::QAttribute *a2); |
| void compareParameters(const QVector<Qt3DRender::QParameter *> ¶ms1, |
| const QVector<Qt3DRender::QParameter *> ¶ms2); |
| void compareRenderPasses(const QVector<Qt3DRender::QRenderPass *> &passes1, |
| const QVector<Qt3DRender::QRenderPass *> &passes2); |
| void compareFilterKeys(const QVector<Qt3DRender::QFilterKey *> &keys1, |
| const QVector<Qt3DRender::QFilterKey *> &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<QString, Qt3DCore::QEntity *> m_entityMap; |
| }; |
| |
| void tst_gltfPlugins::initTestCase() |
| { |
| #ifndef VISUAL_CHECK |
| // QEffect doesn't get registered unless aspects are initialized, generating warnings in |
| // material comparisons. |
| qRegisterMetaType<Qt3DRender::QEffect *>(); |
| #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<Qt3DCore::QEntity *>(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)); |
| createAndAddEntity(QStringLiteral("Cube with DiffuseMap"), mesh, 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); |
| createAndAddEntity(QStringLiteral("Cone with PhongAlpha"), mesh, 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); |
| createAndAddEntity(QStringLiteral("Cylinder with Phong"), mesh, 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); |
| createAndAddEntity(QStringLiteral("Plane with DiffuseSpecularMap"), |
| mesh, 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); |
| createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseMap"), |
| mesh, 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); |
| createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseMapAlpha"), |
| mesh, 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); |
| createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseSpecularMap"), |
| mesh, 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); |
| createAndAddEntity(QStringLiteral("Torus with Gooch"), mesh, 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(); |
| Qt3DRender::QBuffer *colorDataBuffer = |
| new Qt3DRender::QBuffer(boxMesh->geometry()); |
| QByteArray colorBufferData; |
| colorBufferData.resize(8 * 4 * sizeof(float)); |
| |
| float *cPtr = reinterpret_cast<float *>(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->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); |
| createAndAddEntity(QStringLiteral("Child with Phong"), |
| mesh, 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(); |
| Qt3DRender::QBuffer *offsetBuffer = |
| new Qt3DRender::QBuffer(boxMesh->geometry()); |
| QByteArray offsetBufferData; |
| offsetBufferData.resize(8 * 3 * sizeof(float)); |
| |
| float *oPtr = reinterpret_cast<float *>(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); |
| |
| Qt3DRender::QAttribute *customAttribute = new Qt3DRender::QAttribute(); |
| customAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); |
| customAttribute->setBuffer(offsetBuffer); |
| customAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float); |
| customAttribute->setVertexSize(3); |
| customAttribute->setByteOffset(0); |
| customAttribute->setByteStride(0); |
| customAttribute->setCount(8); |
| customAttribute->setName(QStringLiteral("vertexOffset")); |
| |
| boxMesh->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(Qt3DRender::QGeometry *geometry, |
| Qt3DRender::QBuffer *buffer, int count) |
| { |
| Qt3DRender::QAttribute *posAttribute = new Qt3DRender::QAttribute(); |
| posAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); |
| posAttribute->setBuffer(buffer); |
| posAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float); |
| posAttribute->setVertexSize(3); |
| posAttribute->setByteOffset(0); |
| posAttribute->setByteStride(0); |
| posAttribute->setCount(count); |
| posAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName()); |
| |
| geometry->addAttribute(posAttribute); |
| } |
| |
| void tst_gltfPlugins::addIndexAttributeToGeometry(Qt3DRender::QGeometry *geometry, |
| Qt3DRender::QBuffer *buffer, int count) |
| { |
| Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute(); |
| indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); |
| indexAttribute->setBuffer(buffer); |
| indexAttribute->setVertexBaseType(Qt3DRender::QAttribute::UnsignedShort); |
| indexAttribute->setVertexSize(1); |
| indexAttribute->setByteOffset(0); |
| indexAttribute->setByteStride(0); |
| indexAttribute->setCount(count); |
| |
| geometry->addAttribute(indexAttribute); |
| } |
| |
| void tst_gltfPlugins::addColorAttributeToGeometry(Qt3DRender::QGeometry *geometry, |
| Qt3DRender::QBuffer *buffer, int count) |
| { |
| Qt3DRender::QAttribute *colorAttribute = new Qt3DRender::QAttribute(); |
| colorAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); |
| colorAttribute->setBuffer(buffer); |
| colorAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float); |
| colorAttribute->setVertexSize(4); |
| colorAttribute->setByteOffset(0); |
| colorAttribute->setByteStride(0); |
| colorAttribute->setCount(count); |
| colorAttribute->setName(Qt3DRender::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<Qt3DCore::QEntity *>(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<Qt3DCore::QTransform *>(component)) |
| return castedComponent; |
| } |
| return nullptr; |
| } |
| |
| Qt3DRender::QAbstractLight *tst_gltfPlugins::lightComponent(Qt3DCore::QEntity *entity) |
| { |
| for (auto component : entity->components()) { |
| if (auto castedComponent = qobject_cast<Qt3DRender::QAbstractLight *>(component)) |
| return castedComponent; |
| } |
| return nullptr; |
| } |
| |
| Qt3DRender::QCameraLens *tst_gltfPlugins::cameraComponent(Qt3DCore::QEntity *entity) |
| { |
| for (auto component : entity->components()) { |
| if (auto castedComponent = qobject_cast<Qt3DRender::QCameraLens *>(component)) |
| return castedComponent; |
| } |
| return nullptr; |
| } |
| |
| Qt3DRender::QGeometryRenderer *tst_gltfPlugins::meshComponent(Qt3DCore::QEntity *entity) |
| { |
| for (auto component : entity->components()) { |
| if (auto castedComponent = qobject_cast<Qt3DRender::QGeometryRenderer *>(component)) |
| return castedComponent; |
| } |
| return nullptr; |
| } |
| |
| Qt3DRender::QMaterial *tst_gltfPlugins::materialComponent(Qt3DCore::QEntity *entity) |
| { |
| for (auto component : entity->components()) { |
| if (auto castedComponent = qobject_cast<Qt3DRender::QMaterial *>(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<Qt3DCore::QTransform *>(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<Qt3DRender::QGeometryRenderer *>(c1)) { |
| auto mesh2 = qobject_cast<Qt3DRender::QGeometryRenderer *>(c2); |
| QVERIFY(mesh2 != nullptr); |
| auto geometry1 = mesh1->geometry(); |
| auto geometry2 = mesh2->geometry(); |
| // Check that attributes match. |
| compareAttributes( |
| findAttribute(Qt3DRender::QAttribute::defaultPositionAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry1), |
| findAttribute(Qt3DRender::QAttribute::defaultPositionAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry2)); |
| compareAttributes( |
| findAttribute(Qt3DRender::QAttribute::defaultNormalAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry1), |
| findAttribute(Qt3DRender::QAttribute::defaultNormalAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry2)); |
| compareAttributes( |
| findAttribute(Qt3DRender::QAttribute::defaultTangentAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry1), |
| findAttribute(Qt3DRender::QAttribute::defaultTangentAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry2)); |
| compareAttributes( |
| findAttribute(Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry1), |
| findAttribute(Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry2)); |
| compareAttributes( |
| findAttribute(Qt3DRender::QAttribute::defaultColorAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry1), |
| findAttribute(Qt3DRender::QAttribute::defaultColorAttributeName(), |
| Qt3DRender::QAttribute::VertexAttribute, |
| geometry2)); |
| compareAttributes( |
| findAttribute(QStringLiteral(""), |
| Qt3DRender::QAttribute::IndexAttribute, |
| geometry1), |
| findAttribute(QStringLiteral(""), |
| Qt3DRender::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<QColor>(), v2.value<QColor>()); |
| } else if (v1.type() == QVariant::Vector3D) { |
| QCOMPARE(v1.value<QVector3D>(), v2.value<QVector3D>()); |
| } else if (v1.type() == QVariant::Matrix4x4) { |
| QCOMPARE(v1.value<QMatrix4x4>(), v2.value<QMatrix4x4>()); |
| } 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<Qt3DRender::QMaterial *>(c1); |
| auto m2 = qobject_cast<Qt3DRender::QMaterial *>(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); |
| } |
| } |
| } |
| } |
| } |
| |
| Qt3DRender::QAttribute *tst_gltfPlugins::findAttribute(const QString &name, |
| Qt3DRender::QAttribute::AttributeType type, |
| Qt3DRender::QGeometry *geometry) |
| { |
| for (auto att : geometry->attributes()) { |
| if ((type == Qt3DRender::QAttribute::IndexAttribute && type == att->attributeType()) |
| || name == att->name()) { |
| return att; |
| } |
| } |
| return nullptr; |
| } |
| |
| void tst_gltfPlugins::compareAttributes(Qt3DRender::QAttribute *a1, Qt3DRender::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 QVector<Qt3DRender::QParameter *> ¶ms1, |
| const QVector<Qt3DRender::QParameter *> ¶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<QColor>(); |
| QVector4D vec = p2->value().value<QVector4D>(); |
| 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<Qt3DRender::QAbstractTexture *>()) { |
| QUrl u1 = getTextureUrl(p1->value().value<Qt3DRender::QAbstractTexture *>()); |
| QUrl u2 = getTextureUrl(p2->value().value<Qt3DRender::QAbstractTexture *>()); |
| QCOMPARE(u1.fileName(), u2.fileName()); |
| } else { |
| QCOMPARE(p1->value(), p2->value()); |
| } |
| } |
| } |
| QVERIFY(pMatch); |
| } |
| } |
| |
| void tst_gltfPlugins::compareRenderPasses(const QVector<Qt3DRender::QRenderPass *> &passes1, |
| const QVector<Qt3DRender::QRenderPass *> &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()); |
| |
| QVector<Qt3DRender::QRenderState *> states1 = pass1->renderStates(); |
| QVector<Qt3DRender::QRenderState *> 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 QVector<Qt3DRender::QFilterKey *> &keys1, |
| const QVector<Qt3DRender::QFilterKey *> &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<Qt3DRender::QTextureImage *>( |
| tex->textureImages().at(0)); |
| if (img) |
| url = img->source(); |
| } |
| return url; |
| } |
| |
| Qt3DRender::QGeometryRenderer *tst_gltfPlugins::createCustomCube() |
| { |
| Qt3DRender::QGeometryRenderer *boxMesh = new Qt3DRender::QGeometryRenderer; |
| Qt3DRender::QGeometry *boxGeometry = new Qt3DRender::QGeometry(boxMesh); |
| Qt3DRender::QBuffer *boxDataBuffer = |
| new Qt3DRender::QBuffer(boxGeometry); |
| Qt3DRender::QBuffer *indexDataBuffer = |
| new Qt3DRender::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<float *>(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<ushort *>(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); |
| |
| boxMesh->setInstanceCount(1); |
| boxMesh->setIndexOffset(0); |
| boxMesh->setFirstInstance(0); |
| boxMesh->setVertexCount(36); |
| boxMesh->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles); |
| boxMesh->setGeometry(boxGeometry); |
| |
| 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<Qt3DRender::QAbstractTexture *>()->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<Qt3DCore::QEntity *>(child)) { |
| for (auto component : childEntity->components()) { |
| if (auto cameraLens = qobject_cast<Qt3DRender::QCameraLens *>(component)) { |
| if (cameraLens->projectionType() == type) |
| return childEntity; |
| } |
| } |
| if (auto cameraEntity = findCameraChild(childEntity, type)) |
| return cameraEntity; |
| } |
| } |
| return nullptr; |
| } |
| |
| void tst_gltfPlugins::exportAndImport_data() |
| { |
| QTest::addColumn<bool>("binaryJson"); |
| QTest::addColumn<bool>("compactJson"); |
| |
| QTest::newRow("No options") << false << false; |
| #ifndef VISUAL_CHECK |
| QTest::newRow("Binary json") << true << false; |
| QTest::newRow("Compact json") << false << true; |
| QTest::newRow("Binary/Compact json") << true << true; // Compact is ignored in this case |
| #endif |
| } |
| |
| void tst_gltfPlugins::exportAndImport() |
| { |
| QFETCH(bool, binaryJson); |
| 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("binaryJson"), QVariant(binaryJson)); |
| 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<Qt3DRender::QCamera *>(exportedEntity); |
| if (exportedCamera) { |
| Qt3DRender::QCamera *importedCamera = |
| qobject_cast<Qt3DRender::QCamera *>(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" |