| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the Qt3D module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include <QtTest/qtest.h> | 
| 41 | #include <QtCore/qtemporarydir.h> | 
| 42 | #include <QtGui/qimage.h> | 
| 43 |  | 
| 44 | #include <private/qsceneimporter_p.h> | 
| 45 | #include <private/qsceneexportfactory_p.h> | 
| 46 | #include <private/qsceneexporter_p.h> | 
| 47 | #include <private/qsceneimportfactory_p.h> | 
| 48 | #include <private/qsceneimporter_p.h> | 
| 49 |  | 
| 50 | #include <Qt3DCore/qentity.h> | 
| 51 | #include <Qt3DCore/qtransform.h> | 
| 52 |  | 
| 53 | #include <Qt3DRender/qcamera.h> | 
| 54 | #include <Qt3DRender/qcameralens.h> | 
| 55 | #include <Qt3DRender/qtextureimage.h> | 
| 56 | #include <Qt3DRender/qspotlight.h> | 
| 57 | #include <Qt3DRender/qdirectionallight.h> | 
| 58 | #include <Qt3DRender/qpointlight.h> | 
| 59 | #include <Qt3DRender/qattribute.h> | 
| 60 | #include <Qt3DRender/qbuffer.h> | 
| 61 | #include <Qt3DRender/qeffect.h> | 
| 62 | #include <Qt3DRender/qshaderprogram.h> | 
| 63 | #include <Qt3DRender/qtechnique.h> | 
| 64 | #include <Qt3DRender/qparameter.h> | 
| 65 | #include <Qt3DRender/qgraphicsapifilter.h> | 
| 66 | #include <Qt3DRender/qfilterkey.h> | 
| 67 | #include <Qt3DRender/qtexture.h> | 
| 68 | #include <Qt3DRender/qcolormask.h> | 
| 69 | #include <Qt3DRender/qblendequation.h> | 
| 70 |  | 
| 71 | #include <Qt3DExtras/qconemesh.h> | 
| 72 | #include <Qt3DExtras/qcuboidmesh.h> | 
| 73 | #include <Qt3DExtras/qcylindermesh.h> | 
| 74 | #include <Qt3DExtras/qplanemesh.h> | 
| 75 | #include <Qt3DExtras/qspheremesh.h> | 
| 76 | #include <Qt3DExtras/qtorusmesh.h> | 
| 77 | #include <Qt3DExtras/qt3dwindow.h> | 
| 78 | #include <Qt3DExtras/qphongmaterial.h> | 
| 79 | #include <Qt3DExtras/qphongalphamaterial.h> | 
| 80 | #include <Qt3DExtras/qdiffusemapmaterial.h> | 
| 81 | #include <Qt3DExtras/qdiffusespecularmapmaterial.h> | 
| 82 | #include <Qt3DExtras/qnormaldiffusemapmaterial.h> | 
| 83 | #include <Qt3DExtras/qnormaldiffusemapalphamaterial.h> | 
| 84 | #include <Qt3DExtras/qnormaldiffusespecularmapmaterial.h> | 
| 85 | #include <Qt3DExtras/qgoochmaterial.h> | 
| 86 | #include <Qt3DExtras/qpervertexcolormaterial.h> | 
| 87 | #include <Qt3DExtras/qforwardrenderer.h> | 
| 88 |  | 
| 89 | //#define VISUAL_CHECK 5000  // The value indicates the time for visual check in ms | 
| 90 | //#define PRESERVE_EXPORT  // Uncomment to preserve export directory contents for analysis | 
| 91 |  | 
| 92 | class tst_gltfPlugins : public QObject | 
| 93 | { | 
| 94 |     Q_OBJECT | 
| 95 |  | 
| 96 | private Q_SLOTS: | 
| 97 |  | 
| 98 |     void initTestCase(); | 
| 99 |     void init(); | 
| 100 |     void cleanup(); | 
| 101 |     void exportAndImport_data(); | 
| 102 |     void exportAndImport(); | 
| 103 |  | 
| 104 | private: | 
| 105 |     void createTestScene(); | 
| 106 |     Qt3DCore::QEntity *findCameraChild(Qt3DCore::QEntity *entity, | 
| 107 |                                        Qt3DRender::QCameraLens::ProjectionType type); | 
| 108 |     void walkEntity(Qt3DCore::QEntity *entity, int depth); | 
| 109 |     void createAndAddEntity(const QString &name, | 
| 110 |                             Qt3DCore::QComponent *comp1 = nullptr, | 
| 111 |                             Qt3DCore::QComponent *comp2 = nullptr, | 
| 112 |                             Qt3DCore::QComponent *comp3 = nullptr, | 
| 113 |                             Qt3DCore::QEntity *parent = nullptr); | 
| 114 |     void addPositionAttributeToGeometry(Qt3DRender::QGeometry *geometry, | 
| 115 |                                         Qt3DRender::QBuffer *buffer, int count); | 
| 116 |     void addIndexAttributeToGeometry(Qt3DRender::QGeometry *geometry, | 
| 117 |                                      Qt3DRender::QBuffer *buffer, int count); | 
| 118 |     void addColorAttributeToGeometry(Qt3DRender::QGeometry *geometry, | 
| 119 |                                      Qt3DRender::QBuffer *buffer, int count); | 
| 120 |     Qt3DCore::QEntity *findChildEntity(Qt3DCore::QEntity *entity, const QString &name); | 
| 121 |     Qt3DCore::QTransform *transformComponent(Qt3DCore::QEntity *entity); | 
| 122 |     Qt3DRender::QAbstractLight *lightComponent(Qt3DCore::QEntity *entity); | 
| 123 |     Qt3DRender::QCameraLens *cameraComponent(Qt3DCore::QEntity *entity); | 
| 124 |     Qt3DRender::QGeometryRenderer *meshComponent(Qt3DCore::QEntity *entity); | 
| 125 |     Qt3DRender::QMaterial *materialComponent(Qt3DCore::QEntity *entity); | 
| 126 |     void compareComponents(Qt3DCore::QComponent *c1, Qt3DCore::QComponent *c2); | 
| 127 |     Qt3DRender::QAttribute *findAttribute(const QString &name, | 
| 128 |                                           Qt3DRender::QAttribute::AttributeType type, | 
| 129 |                                           Qt3DRender::QGeometry *geometry); | 
| 130 |     void compareAttributes(Qt3DRender::QAttribute *a1, Qt3DRender::QAttribute *a2); | 
| 131 |     void compareParameters(const QVector<Qt3DRender::QParameter *> ¶ms1, | 
| 132 |                            const QVector<Qt3DRender::QParameter *> ¶ms2); | 
| 133 |     void compareRenderPasses(const QVector<Qt3DRender::QRenderPass *> &passes1, | 
| 134 |                              const QVector<Qt3DRender::QRenderPass *> &passes2); | 
| 135 |     void compareFilterKeys(const QVector<Qt3DRender::QFilterKey *> &keys1, | 
| 136 |                            const QVector<Qt3DRender::QFilterKey *> &keys2); | 
| 137 |     QUrl getTextureUrl(Qt3DRender::QAbstractTexture *tex); | 
| 138 |     Qt3DRender::QGeometryRenderer *createCustomCube(); | 
| 139 |     Qt3DRender::QEffect *createOnTopEffect(); | 
| 140 |  | 
| 141 |     QTemporaryDir *m_exportDir; | 
| 142 | #ifdef VISUAL_CHECK | 
| 143 |     Qt3DExtras::Qt3DWindow *m_view1; | 
| 144 |     Qt3DExtras::Qt3DWindow *m_view2; | 
| 145 | #endif | 
| 146 |     Qt3DCore::QEntity *m_sceneRoot1; | 
| 147 |     Qt3DCore::QEntity *m_sceneRoot2; | 
| 148 |     QHash<QString, Qt3DCore::QEntity *> m_entityMap; | 
| 149 | }; | 
| 150 |  | 
| 151 | void tst_gltfPlugins::initTestCase() | 
| 152 | { | 
| 153 | #ifndef VISUAL_CHECK | 
| 154 |     // QEffect doesn't get registered unless aspects are initialized, generating warnings in | 
| 155 |     // material comparisons. | 
| 156 |     qRegisterMetaType<Qt3DRender::QEffect *>(); | 
| 157 | #endif | 
| 158 | } | 
| 159 |  | 
| 160 | void tst_gltfPlugins::init() | 
| 161 | { | 
| 162 |     m_exportDir = new QTemporaryDir; | 
| 163 | #ifdef VISUAL_CHECK | 
| 164 |     m_view1 = new Qt3DExtras::Qt3DWindow; | 
| 165 |     m_view1->setTitle(QStringLiteral("Original scene" )); | 
| 166 |     m_view2 = new Qt3DExtras::Qt3DWindow; | 
| 167 |     m_view2->setTitle(QStringLiteral("Imported scene" )); | 
| 168 | #endif | 
| 169 | } | 
| 170 |  | 
| 171 | void tst_gltfPlugins::cleanup() | 
| 172 | { | 
| 173 |     delete m_sceneRoot1; | 
| 174 |     delete m_sceneRoot2; | 
| 175 |     m_entityMap.clear(); | 
| 176 | #ifdef VISUAL_CHECK | 
| 177 |     delete m_view1; | 
| 178 |     delete m_view2; | 
| 179 | #endif | 
| 180 |     delete m_exportDir; | 
| 181 |     // Make sure the slate is clean for the next case | 
| 182 |     QCoreApplication::processEvents(); | 
| 183 |     QTest::qWait(ms: 0); | 
| 184 | } | 
| 185 |  | 
| 186 | void tst_gltfPlugins::walkEntity(Qt3DCore::QEntity *entity, int depth) | 
| 187 | { | 
| 188 |     QString indent; | 
| 189 |     indent.fill(c: ' ', size: depth * 2); | 
| 190 |     qDebug().noquote() << indent << "Entity:"  << entity << "Components:"  << entity->components(); | 
| 191 |     for (auto child : entity->children()) { | 
| 192 |         if (auto childEntity = qobject_cast<Qt3DCore::QEntity *>(object: child)) | 
| 193 |             walkEntity(entity: childEntity, depth: depth + 1); | 
| 194 |     } | 
| 195 | } | 
| 196 |  | 
| 197 | void tst_gltfPlugins::createAndAddEntity(const QString &name, | 
| 198 |                                          Qt3DCore::QComponent *comp1, | 
| 199 |                                          Qt3DCore::QComponent *comp2, | 
| 200 |                                          Qt3DCore::QComponent *comp3, | 
| 201 |                                          Qt3DCore::QEntity *parent) | 
| 202 | { | 
| 203 |     Qt3DCore::QEntity *parentEntity = parent ? parent : m_sceneRoot1; | 
| 204 |     Qt3DCore::QEntity *entity = new Qt3DCore::QEntity(parentEntity); | 
| 205 |     entity->setObjectName(name); | 
| 206 |     if (comp1) { | 
| 207 |         entity->addComponent(comp: comp1); | 
| 208 |         comp1->setObjectName(comp1->metaObject()->className()); | 
| 209 |     } | 
| 210 |     if (comp2) { | 
| 211 |         entity->addComponent(comp: comp2); | 
| 212 |         comp2->setObjectName(comp2->metaObject()->className()); | 
| 213 |     } | 
| 214 |     if (comp3) { | 
| 215 |         entity->addComponent(comp: comp3); | 
| 216 |         comp3->setObjectName(comp3->metaObject()->className()); | 
| 217 |     } | 
| 218 |  | 
| 219 |     m_entityMap.insert(akey: name, avalue: entity); | 
| 220 | } | 
| 221 |  | 
| 222 | void tst_gltfPlugins::createTestScene() | 
| 223 | { | 
| 224 | #ifdef VISUAL_CHECK | 
| 225 |     m_view1->defaultFrameGraph()->setClearColor(Qt::lightGray); | 
| 226 |     m_view2->defaultFrameGraph()->setClearColor(Qt::lightGray); | 
| 227 | #endif | 
| 228 |     m_sceneRoot1 = new Qt3DCore::QEntity(); | 
| 229 |     m_sceneRoot1->setObjectName(QStringLiteral("Scene root" )); | 
| 230 |     m_sceneRoot2 = new Qt3DCore::QEntity(); | 
| 231 |     m_sceneRoot2->setObjectName(QStringLiteral("Imported scene parent" )); | 
| 232 |  | 
| 233 |     // Perspective camera | 
| 234 |     { | 
| 235 |         Qt3DRender::QCamera *camera = new Qt3DRender::QCamera(m_sceneRoot1); | 
| 236 |         camera->setProjectionType(Qt3DRender::QCameraLens::PerspectiveProjection); | 
| 237 |         camera->setViewCenter(QVector3D(0.0f, 1.5f, 0.0f)); | 
| 238 |         camera->setPosition(QVector3D(0.0f, 3.5f, 15.0f)); | 
| 239 |         camera->setNearPlane(0.001f); | 
| 240 |         camera->setFarPlane(10000.0f); | 
| 241 |         camera->setObjectName(QStringLiteral("Main camera" )); | 
| 242 |         camera->transform()->setObjectName(QStringLiteral("Main camera transform" )); | 
| 243 |         camera->lens()->setObjectName(QStringLiteral("Main camera lens" )); | 
| 244 |         camera->setFieldOfView(30.0f); | 
| 245 |         camera->setAspectRatio(1.0f); | 
| 246 |         m_entityMap.insert(akey: camera->objectName(), avalue: camera); | 
| 247 |     } | 
| 248 |     // Ortho camera | 
| 249 |     { | 
| 250 |         Qt3DCore::QEntity *camera = new Qt3DCore::QEntity(m_sceneRoot1); | 
| 251 |         camera->setObjectName(QStringLiteral("Ortho camera" )); | 
| 252 |  | 
| 253 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 254 |         transform->setTranslation(QVector3D(0.0f, 0.0f, -15.0f)); | 
| 255 |         transform->setObjectName(QStringLiteral("Ortho camera transform" )); | 
| 256 |         camera->addComponent(comp: transform); | 
| 257 |  | 
| 258 |         Qt3DRender::QCameraLens *lens = new Qt3DRender::QCameraLens; | 
| 259 |         lens->setProjectionType(Qt3DRender::QCameraLens::OrthographicProjection); | 
| 260 |         lens->setNearPlane(0.001f); | 
| 261 |         lens->setFarPlane(10000.0f); | 
| 262 |         lens->setRight(7.0f); | 
| 263 |         lens->setLeft(-7.0f); | 
| 264 |         lens->setTop(5.0f); | 
| 265 |         lens->setBottom(-5.0f); | 
| 266 |         lens->setObjectName(QStringLiteral("Ortho camera lens" )); | 
| 267 |         camera->addComponent(comp: lens); | 
| 268 |  | 
| 269 |         m_entityMap.insert(akey: camera->objectName(), avalue: camera); | 
| 270 | #ifdef VISUAL_CHECK | 
| 271 |         m_view1->defaultFrameGraph()->setCamera(camera); | 
| 272 | #endif | 
| 273 |     } | 
| 274 |  | 
| 275 |     // Point light | 
| 276 |     { | 
| 277 |         Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight; | 
| 278 |         light->setColor(QColor("#FFDDAA" )); | 
| 279 |         light->setIntensity(0.9f); | 
| 280 |         light->setConstantAttenuation(0.03f); | 
| 281 |         light->setLinearAttenuation(0.04f); | 
| 282 |         light->setQuadraticAttenuation(0.01f); | 
| 283 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 284 |         transform->setTranslation(QVector3D(-6.0f, 2.0f, 3.0f)); | 
| 285 |         createAndAddEntity(QStringLiteral("Point Light" ), comp1: light, comp2: transform); | 
| 286 |     } | 
| 287 |     // Directional light | 
| 288 |     { | 
| 289 |         Qt3DRender::QDirectionalLight *light = new Qt3DRender::QDirectionalLight; | 
| 290 |         light->setColor(QColor("#BBCCEE" )); | 
| 291 |         light->setIntensity(0.75f); | 
| 292 |         light->setWorldDirection(QVector3D(-1.0f, -1.0f, -1.0f)); | 
| 293 |         createAndAddEntity(QStringLiteral("Directional Light" ), comp1: light); | 
| 294 |     } | 
| 295 |     // Spot light | 
| 296 |     { | 
| 297 |         Qt3DRender::QSpotLight *light = new Qt3DRender::QSpotLight; | 
| 298 |         light->setColor(QColor("#5599DD" )); | 
| 299 |         light->setIntensity(2.0f); | 
| 300 |         light->setConstantAttenuation(0.03f); | 
| 301 |         light->setLinearAttenuation(0.04f); | 
| 302 |         light->setQuadraticAttenuation(0.01f); | 
| 303 |         light->setLocalDirection(QVector3D(0.0f, -1.0f, -1.0f)); | 
| 304 |         light->setCutOffAngle(30.0f); | 
| 305 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 306 |         transform->setTranslation(QVector3D(0.0f, 5.0f, 0.0f)); | 
| 307 |         createAndAddEntity(QStringLiteral("Spot Light" ), comp1: light, comp2: transform); | 
| 308 |     } | 
| 309 |     // Cube with DiffuseMap | 
| 310 |     { | 
| 311 |         Qt3DExtras::QDiffuseMapMaterial *material = new Qt3DExtras::QDiffuseMapMaterial(); | 
| 312 |         Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); | 
| 313 |         material->diffuse()->addTextureImage(textureImage: diffuseTextureImage); | 
| 314 |         material->setAmbient(QColor("#000088" )); | 
| 315 |         material->setSpecular(QColor("#FFFF00" )); | 
| 316 |         material->setShininess(30.0); | 
| 317 |         material->setTextureScale(2.0f); | 
| 318 |         diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png" ))); | 
| 319 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 320 |         transform->setScale(0.75f); | 
| 321 |         transform->setTranslation(QVector3D(2.0f, 1.0f, -1.0f)); | 
| 322 |         Qt3DExtras::QCuboidMesh *mesh = new Qt3DExtras::QCuboidMesh; | 
| 323 |         mesh->setXExtent(1.2f); | 
| 324 |         mesh->setYExtent(1.1f); | 
| 325 |         mesh->setZExtent(0.9f); | 
| 326 |         mesh->setYZMeshResolution(QSize(2, 2)); | 
| 327 |         mesh->setYZMeshResolution(QSize(2, 3)); | 
| 328 |         mesh->setYZMeshResolution(QSize(3, 2)); | 
| 329 |         createAndAddEntity(QStringLiteral("Cube with DiffuseMap" ), comp1: mesh, comp2: material, comp3: transform); | 
| 330 |     } | 
| 331 |     // Cone with PhongAlpha | 
| 332 |     { | 
| 333 |         Qt3DExtras::QPhongAlphaMaterial *material = new Qt3DExtras::QPhongAlphaMaterial(); | 
| 334 |         material->setAlpha(0.6f); | 
| 335 |         material->setAmbient(QColor("#550000" )); | 
| 336 |         material->setDiffuse(QColor("#00FFFF" )); | 
| 337 |         material->setSpecular(QColor("#FFFF00" )); | 
| 338 |         material->setShininess(20.0f); | 
| 339 |         material->setSourceRgbArg(Qt3DRender::QBlendEquationArguments::Source1Color); | 
| 340 |         material->setSourceAlphaArg(Qt3DRender::QBlendEquationArguments::Source1Alpha); | 
| 341 |         material->setDestinationRgbArg(Qt3DRender::QBlendEquationArguments::DestinationColor); | 
| 342 |         material->setDestinationAlphaArg(Qt3DRender::QBlendEquationArguments::DestinationAlpha); | 
| 343 |         material->setBlendFunctionArg(Qt3DRender::QBlendEquation::ReverseSubtract); | 
| 344 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 345 |         transform->setTranslation(QVector3D(2.0f, 1.0f, 1.0f)); | 
| 346 |         transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: -20.0f)); | 
| 347 |         Qt3DExtras::QConeMesh *mesh = new Qt3DExtras::QConeMesh; | 
| 348 |         mesh->setRings(2); | 
| 349 |         mesh->setSlices(16); | 
| 350 |         mesh->setTopRadius(0.5f); | 
| 351 |         mesh->setBottomRadius(1.5f); | 
| 352 |         mesh->setLength(0.9f); | 
| 353 |         createAndAddEntity(QStringLiteral("Cone with PhongAlpha" ), comp1: mesh, comp2: material, comp3: transform); | 
| 354 |     } | 
| 355 |     // Cylinder with Phong | 
| 356 |     { | 
| 357 |         Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial(); | 
| 358 |         material->setAmbient(QColor("#220022" )); | 
| 359 |         material->setDiffuse(QColor("#6633AA" )); | 
| 360 |         material->setSpecular(QColor("#66AA33" )); | 
| 361 |         material->setShininess(50.0f); | 
| 362 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 363 |         transform->setTranslation(QVector3D(0.0f, 1.0f, 1.0f)); | 
| 364 |         transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: -45.0f)); | 
| 365 |         Qt3DExtras::QCylinderMesh *mesh = new Qt3DExtras::QCylinderMesh; | 
| 366 |         mesh->setRadius(0.5f); | 
| 367 |         mesh->setRings(3); | 
| 368 |         mesh->setLength(1.2f); | 
| 369 |         mesh->setSlices(16); | 
| 370 |         createAndAddEntity(QStringLiteral("Cylinder with Phong" ), comp1: mesh, comp2: material, comp3: transform); | 
| 371 |     } | 
| 372 |     // Plane with DiffuseSpecularMap | 
| 373 |     { | 
| 374 |         Qt3DExtras::QDiffuseSpecularMapMaterial *material = | 
| 375 |                 new Qt3DExtras::QDiffuseSpecularMapMaterial(); | 
| 376 |         Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); | 
| 377 |         material->diffuse()->addTextureImage(textureImage: diffuseTextureImage); | 
| 378 |         diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png" ))); | 
| 379 |         Qt3DRender::QTextureImage *specularTextureImage = new Qt3DRender::QTextureImage(); | 
| 380 |         material->specular()->addTextureImage(textureImage: specularTextureImage); | 
| 381 |         specularTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_specular.png" ))); | 
| 382 |  | 
| 383 |         material->setAmbient(QColor("#0000FF" )); | 
| 384 |         material->setTextureScale(3.0f); | 
| 385 |         material->setShininess(15.0f); | 
| 386 |  | 
| 387 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 388 |         transform->setTranslation(QVector3D(-1.0f, 1.0f, 1.0f)); | 
| 389 |         transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(x: 1.0f, y: 0.0f, z: 0.0f, angle: 45.0f)); | 
| 390 |  | 
| 391 |         Qt3DExtras::QPlaneMesh *mesh = new Qt3DExtras::QPlaneMesh; | 
| 392 |         mesh->setMeshResolution(QSize(3, 3)); | 
| 393 |         mesh->setHeight(1.5f); | 
| 394 |         mesh->setWidth(1.2f); | 
| 395 |         createAndAddEntity(QStringLiteral("Plane with DiffuseSpecularMap" ), | 
| 396 |                            comp1: mesh, comp2: material, comp3: transform); | 
| 397 |     } | 
| 398 |     // Sphere with NormalDiffuseMap | 
| 399 |     { | 
| 400 |         Qt3DExtras::QNormalDiffuseMapMaterial *material = | 
| 401 |                 new Qt3DExtras::QNormalDiffuseMapMaterial(); | 
| 402 |  | 
| 403 |         Qt3DRender::QTextureImage *normalTextureImage = new Qt3DRender::QTextureImage(); | 
| 404 |         material->normal()->addTextureImage(textureImage: normalTextureImage); | 
| 405 |         normalTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_normal.png" ))); | 
| 406 |  | 
| 407 |         Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); | 
| 408 |         material->diffuse()->addTextureImage(textureImage: diffuseTextureImage); | 
| 409 |         diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png" ))); | 
| 410 |  | 
| 411 |         material->setAmbient(QColor("#000044" )); | 
| 412 |         material->setSpecular(QColor("#0000CC" )); | 
| 413 |         material->setShininess(9.0f); | 
| 414 |         material->setTextureScale(4.0f); | 
| 415 |  | 
| 416 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 417 |         transform->setTranslation(QVector3D(0.0f, 1.0f, -10.0f)); | 
| 418 |  | 
| 419 |         Qt3DExtras::QSphereMesh *mesh = new Qt3DExtras::QSphereMesh; | 
| 420 |         mesh->setRadius(2.0f); | 
| 421 |         mesh->setRings(16); | 
| 422 |         mesh->setSlices(16); | 
| 423 |         mesh->setGenerateTangents(true); | 
| 424 |         createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseMap" ), | 
| 425 |                            comp1: mesh, comp2: material, comp3: transform); | 
| 426 |     } | 
| 427 |     // Sphere with NormalDiffuseMapAlpha | 
| 428 |     { | 
| 429 |         Qt3DExtras::QNormalDiffuseMapAlphaMaterial *material = | 
| 430 |                 new Qt3DExtras::QNormalDiffuseMapAlphaMaterial(); | 
| 431 |  | 
| 432 |         Qt3DRender::QTextureImage *normalTextureImage = new Qt3DRender::QTextureImage(); | 
| 433 |         material->normal()->addTextureImage(textureImage: normalTextureImage); | 
| 434 |         normalTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_normal.png" ))); | 
| 435 |  | 
| 436 |         Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); | 
| 437 |         material->diffuse()->addTextureImage(textureImage: diffuseTextureImage); | 
| 438 |         diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_with_alpha.png" ))); | 
| 439 |  | 
| 440 |         material->setAmbient(QColor("#000044" )); | 
| 441 |         material->setSpecular(QColor("#0000CC" )); | 
| 442 |         material->setShininess(9.0f); | 
| 443 |         material->setTextureScale(4.0f); | 
| 444 |  | 
| 445 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 446 |         transform->setTranslation(QVector3D(4.0f, 1.0f, -10.0f)); | 
| 447 |  | 
| 448 |         Qt3DExtras::QSphereMesh *mesh = new Qt3DExtras::QSphereMesh; | 
| 449 |         mesh->setRadius(2.0f); | 
| 450 |         mesh->setRings(16); | 
| 451 |         mesh->setSlices(16); | 
| 452 |         mesh->setGenerateTangents(true); | 
| 453 |         createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseMapAlpha" ), | 
| 454 |                            comp1: mesh, comp2: material, comp3: transform); | 
| 455 |     } | 
| 456 |     // Sphere with NormalDiffuseSpecularMap | 
| 457 |     { | 
| 458 |         Qt3DExtras::QNormalDiffuseSpecularMapMaterial *material = | 
| 459 |                 new Qt3DExtras::QNormalDiffuseSpecularMapMaterial(); | 
| 460 |  | 
| 461 |         Qt3DRender::QTextureImage *normalTextureImage = new Qt3DRender::QTextureImage(); | 
| 462 |         material->normal()->addTextureImage(textureImage: normalTextureImage); | 
| 463 |         normalTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_normal.png" ))); | 
| 464 |  | 
| 465 |         Qt3DRender::QTextureImage *diffuseTextureImage = new Qt3DRender::QTextureImage(); | 
| 466 |         material->diffuse()->addTextureImage(textureImage: diffuseTextureImage); | 
| 467 |         diffuseTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png" ))); | 
| 468 |  | 
| 469 |         Qt3DRender::QTextureImage *specularTextureImage = new Qt3DRender::QTextureImage(); | 
| 470 |         material->specular()->addTextureImage(textureImage: specularTextureImage); | 
| 471 |         specularTextureImage->setSource(QUrl(QStringLiteral("qrc:/qtlogo_specular.png" ))); | 
| 472 |  | 
| 473 |         material->setAmbient(QColor("#000044" )); | 
| 474 |         material->setShininess(9.0f); | 
| 475 |         material->setTextureScale(4.0f); | 
| 476 |  | 
| 477 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 478 |         transform->setTranslation(QVector3D(-4.0f, 1.0f, -10.0f)); | 
| 479 |  | 
| 480 |         Qt3DExtras::QSphereMesh *mesh = new Qt3DExtras::QSphereMesh; | 
| 481 |         mesh->setRadius(2.0f); | 
| 482 |         mesh->setRings(16); | 
| 483 |         mesh->setSlices(16); | 
| 484 |         mesh->setGenerateTangents(true); | 
| 485 |         createAndAddEntity(QStringLiteral("Sphere with NormalDiffuseSpecularMap" ), | 
| 486 |                            comp1: mesh, comp2: material, comp3: transform); | 
| 487 |     } | 
| 488 |     // Torus with Gooch | 
| 489 |     { | 
| 490 |         Qt3DExtras::QGoochMaterial *material = new Qt3DExtras::QGoochMaterial(); | 
| 491 |  | 
| 492 |         material->setDiffuse(QColor("#333333" )); | 
| 493 |         material->setSpecular(QColor("#550055" )); | 
| 494 |         material->setCool(QColor("#0055AA" )); | 
| 495 |         material->setWarm(QColor("#FF3300" )); | 
| 496 |         material->setAlpha(0.2f); | 
| 497 |         material->setBeta(0.4f); | 
| 498 |         material->setShininess(22.0f); | 
| 499 |  | 
| 500 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 501 |         transform->setTranslation(QVector3D(0.0f, 4.0f, -10.0f)); | 
| 502 |  | 
| 503 |         Qt3DExtras::QTorusMesh *mesh = new Qt3DExtras::QTorusMesh; | 
| 504 |         mesh->setRadius(1.0f); | 
| 505 |         mesh->setMinorRadius(0.5f); | 
| 506 |         mesh->setRings(16); | 
| 507 |         mesh->setSlices(16); | 
| 508 |         createAndAddEntity(QStringLiteral("Torus with Gooch" ), comp1: mesh, comp2: material, comp3: transform); | 
| 509 |     } | 
| 510 |     // Custom cube with per-vertex colors | 
| 511 |     { | 
| 512 |         Qt3DExtras::QPerVertexColorMaterial *material = new Qt3DExtras::QPerVertexColorMaterial(); | 
| 513 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 514 |         transform->setTranslation(QVector3D(4.0f, 3.0f, -15.0f)); | 
| 515 |         transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(x: 1.0f, y: 1.0f, z: 1.0f, angle: 270.0f)); | 
| 516 |  | 
| 517 |         Qt3DRender::QGeometryRenderer *boxMesh = createCustomCube(); | 
| 518 |         Qt3DRender::QBuffer *colorDataBuffer = | 
| 519 |                 new Qt3DRender::QBuffer(boxMesh->geometry()); | 
| 520 |         QByteArray colorBufferData; | 
| 521 |         colorBufferData.resize(size: 8 * 4 * sizeof(float)); | 
| 522 |  | 
| 523 |         float *cPtr = reinterpret_cast<float *>(colorBufferData.data()); | 
| 524 |         for (int i = 0; i < 8; i++) { | 
| 525 |             cPtr[i * 4] = float(i) / 8.0f; | 
| 526 |             cPtr[i * 4 + 1] = float(8 - i) / 8.0f; | 
| 527 |             cPtr[i * 4 + 2] = float((i + 4) % 8) / 8.0f; | 
| 528 |             cPtr[i * 4 + 3] = 1.0f; | 
| 529 |         } | 
| 530 |  | 
| 531 |         colorDataBuffer->setData(colorBufferData); | 
| 532 |  | 
| 533 |         addColorAttributeToGeometry(geometry: boxMesh->geometry(), buffer: colorDataBuffer, count: 8); | 
| 534 |  | 
| 535 |         createAndAddEntity(QStringLiteral("Custom cube with per-vertex colors" ), | 
| 536 |                            comp1: boxMesh, comp2: material, comp3: transform); | 
| 537 |     } | 
| 538 |     // Child cylinder with Phong | 
| 539 |     { | 
| 540 |         Qt3DCore::QEntity *parentEntity = findChildEntity(entity: m_sceneRoot1, | 
| 541 |                                                           QStringLiteral("Cylinder with Phong" )); | 
| 542 |         Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial(); | 
| 543 |         material->setAmbient(QColor("#333333" )); | 
| 544 |         material->setDiffuse(QColor("#88FF00" )); | 
| 545 |         material->setSpecular(QColor("#000088" )); | 
| 546 |         material->setShininess(150.0f); | 
| 547 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 548 |         transform->setTranslation(QVector3D(0.0f, 4.0f, 0.0f)); | 
| 549 |         Qt3DExtras::QCylinderMesh *mesh = new Qt3DExtras::QCylinderMesh; | 
| 550 |         mesh->setRadius(0.25f); | 
| 551 |         mesh->setRings(3); | 
| 552 |         mesh->setLength(1.5f); | 
| 553 |         mesh->setSlices(16); | 
| 554 |         createAndAddEntity(QStringLiteral("Child with Phong" ), | 
| 555 |                            comp1: mesh, comp2: material, comp3: transform, parent: parentEntity); | 
| 556 |     } | 
| 557 |     // Cube with custom material | 
| 558 |     { | 
| 559 |         Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial; | 
| 560 |         material->setEffect(createOnTopEffect()); | 
| 561 |         material->addParameter(parameter: new Qt3DRender::QParameter(QStringLiteral("globalOffset" ), | 
| 562 |                                                           QVector3D(-3.0f, 0.0f, 3.0f))); | 
| 563 |         material->addParameter(parameter: new Qt3DRender::QParameter(QStringLiteral("extraYOffset" ), 3)); | 
| 564 |         material->effect()->addParameter(parameter: new Qt3DRender::QParameter(QStringLiteral("handleColor" ), | 
| 565 |                                                                     QColor(Qt::magenta))); | 
| 566 |         material->effect()->addParameter(parameter: new Qt3DRender::QParameter(QStringLiteral("reverseOffset" ), | 
| 567 |                                                                     QVariant::fromValue(value: true))); | 
| 568 |  | 
| 569 |         Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; | 
| 570 |         transform->setTranslation(QVector3D(0.0f, 2.0f, -40.0f)); | 
| 571 |         transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(x: 1.0f, y: 2.0f, z: 3.0f, angle: 90.0f)); | 
| 572 |         Qt3DRender::QGeometryRenderer *boxMesh = createCustomCube(); | 
| 573 |         Qt3DRender::QBuffer *offsetBuffer = | 
| 574 |                 new Qt3DRender::QBuffer(boxMesh->geometry()); | 
| 575 |         QByteArray offsetBufferData; | 
| 576 |         offsetBufferData.resize(size: 8 * 3 * sizeof(float)); | 
| 577 |  | 
| 578 |         float *oPtr = reinterpret_cast<float *>(offsetBufferData.data()); | 
| 579 |         for (int i = 0; i < 8; i++) { | 
| 580 |             oPtr[i * 3] = float(i) / 4.0f; | 
| 581 |             oPtr[i * 3 + 1] = float(8 - i) / 4.0f + 2.0f; | 
| 582 |             oPtr[i * 3 + 2] = float((i + 4) % 8) / 4.0f; | 
| 583 |         } | 
| 584 |  | 
| 585 |         offsetBuffer->setData(offsetBufferData); | 
| 586 |  | 
| 587 |         Qt3DRender::QAttribute *customAttribute = new Qt3DRender::QAttribute(); | 
| 588 |         customAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); | 
| 589 |         customAttribute->setBuffer(offsetBuffer); | 
| 590 |         customAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float); | 
| 591 |         customAttribute->setVertexSize(3); | 
| 592 |         customAttribute->setByteOffset(0); | 
| 593 |         customAttribute->setByteStride(0); | 
| 594 |         customAttribute->setCount(8); | 
| 595 |         customAttribute->setName(QStringLiteral("vertexOffset" )); | 
| 596 |  | 
| 597 |         boxMesh->geometry()->addAttribute(attribute: customAttribute); | 
| 598 |  | 
| 599 |         createAndAddEntity(QStringLiteral("Custom cube with on-top material" ), | 
| 600 |                            comp1: boxMesh, comp2: material, comp3: transform); | 
| 601 |     } | 
| 602 |  | 
| 603 | #ifdef VISUAL_CHECK | 
| 604 |     m_view1->setGeometry(30, 30, 400, 400); | 
| 605 |     m_view1->setRootEntity(m_sceneRoot1); | 
| 606 |     m_view1->show(); | 
| 607 |  | 
| 608 |     m_view2->setGeometry(450, 30, 400, 400); | 
| 609 |     m_view2->setRootEntity(m_sceneRoot2); | 
| 610 |     m_view2->show(); | 
| 611 |  | 
| 612 |     QTest::qWaitForWindowExposed(m_view1); | 
| 613 |     QTest::qWaitForWindowExposed(m_view2); | 
| 614 | #endif | 
| 615 | } | 
| 616 |  | 
| 617 | void tst_gltfPlugins::addPositionAttributeToGeometry(Qt3DRender::QGeometry *geometry, | 
| 618 |                                                      Qt3DRender::QBuffer *buffer, int count) | 
| 619 | { | 
| 620 |     Qt3DRender::QAttribute *posAttribute = new Qt3DRender::QAttribute(); | 
| 621 |     posAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); | 
| 622 |     posAttribute->setBuffer(buffer); | 
| 623 |     posAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float); | 
| 624 |     posAttribute->setVertexSize(3); | 
| 625 |     posAttribute->setByteOffset(0); | 
| 626 |     posAttribute->setByteStride(0); | 
| 627 |     posAttribute->setCount(count); | 
| 628 |     posAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName()); | 
| 629 |  | 
| 630 |     geometry->addAttribute(attribute: posAttribute); | 
| 631 | } | 
| 632 |  | 
| 633 | void tst_gltfPlugins::addIndexAttributeToGeometry(Qt3DRender::QGeometry *geometry, | 
| 634 |                                                   Qt3DRender::QBuffer *buffer, int count) | 
| 635 | { | 
| 636 |     Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute(); | 
| 637 |     indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); | 
| 638 |     indexAttribute->setBuffer(buffer); | 
| 639 |     indexAttribute->setVertexBaseType(Qt3DRender::QAttribute::UnsignedShort); | 
| 640 |     indexAttribute->setVertexSize(1); | 
| 641 |     indexAttribute->setByteOffset(0); | 
| 642 |     indexAttribute->setByteStride(0); | 
| 643 |     indexAttribute->setCount(count); | 
| 644 |  | 
| 645 |     geometry->addAttribute(attribute: indexAttribute); | 
| 646 | } | 
| 647 |  | 
| 648 | void tst_gltfPlugins::addColorAttributeToGeometry(Qt3DRender::QGeometry *geometry, | 
| 649 |                                                   Qt3DRender::QBuffer *buffer, int count) | 
| 650 | { | 
| 651 |     Qt3DRender::QAttribute *colorAttribute = new Qt3DRender::QAttribute(); | 
| 652 |     colorAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); | 
| 653 |     colorAttribute->setBuffer(buffer); | 
| 654 |     colorAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float); | 
| 655 |     colorAttribute->setVertexSize(4); | 
| 656 |     colorAttribute->setByteOffset(0); | 
| 657 |     colorAttribute->setByteStride(0); | 
| 658 |     colorAttribute->setCount(count); | 
| 659 |     colorAttribute->setName(Qt3DRender::QAttribute::defaultColorAttributeName()); | 
| 660 |  | 
| 661 |     geometry->addAttribute(attribute: colorAttribute); | 
| 662 | } | 
| 663 |  | 
| 664 | Qt3DCore::QEntity *tst_gltfPlugins::findChildEntity(Qt3DCore::QEntity *entity, const QString &name) | 
| 665 | { | 
| 666 |     for (auto child : entity->children()) { | 
| 667 |         if (auto childEntity = qobject_cast<Qt3DCore::QEntity *>(object: child)) { | 
| 668 |             if (childEntity->objectName() == name) | 
| 669 |                 return childEntity; | 
| 670 |             if (auto foundEntity = findChildEntity(entity: childEntity, name)) | 
| 671 |                 return foundEntity; | 
| 672 |         } | 
| 673 |     } | 
| 674 |     return nullptr; | 
| 675 | } | 
| 676 |  | 
| 677 | Qt3DCore::QTransform *tst_gltfPlugins::transformComponent(Qt3DCore::QEntity *entity) | 
| 678 | { | 
| 679 |     for (auto component : entity->components()) { | 
| 680 |         if (auto castedComponent = qobject_cast<Qt3DCore::QTransform *>(object: component)) | 
| 681 |             return castedComponent; | 
| 682 |     } | 
| 683 |     return nullptr; | 
| 684 | } | 
| 685 |  | 
| 686 | Qt3DRender::QAbstractLight *tst_gltfPlugins::lightComponent(Qt3DCore::QEntity *entity) | 
| 687 | { | 
| 688 |     for (auto component : entity->components()) { | 
| 689 |         if (auto castedComponent = qobject_cast<Qt3DRender::QAbstractLight *>(object: component)) | 
| 690 |             return castedComponent; | 
| 691 |     } | 
| 692 |     return nullptr; | 
| 693 | } | 
| 694 |  | 
| 695 | Qt3DRender::QCameraLens *tst_gltfPlugins::cameraComponent(Qt3DCore::QEntity *entity) | 
| 696 | { | 
| 697 |     for (auto component : entity->components()) { | 
| 698 |         if (auto castedComponent = qobject_cast<Qt3DRender::QCameraLens *>(object: component)) | 
| 699 |             return castedComponent; | 
| 700 |     } | 
| 701 |     return nullptr; | 
| 702 | } | 
| 703 |  | 
| 704 | Qt3DRender::QGeometryRenderer *tst_gltfPlugins::meshComponent(Qt3DCore::QEntity *entity) | 
| 705 | { | 
| 706 |     for (auto component : entity->components()) { | 
| 707 |         if (auto castedComponent = qobject_cast<Qt3DRender::QGeometryRenderer *>(object: component)) | 
| 708 |             return castedComponent; | 
| 709 |     } | 
| 710 |     return nullptr; | 
| 711 | } | 
| 712 |  | 
| 713 | Qt3DRender::QMaterial *tst_gltfPlugins::materialComponent(Qt3DCore::QEntity *entity) | 
| 714 | { | 
| 715 |     for (auto component : entity->components()) { | 
| 716 |         if (auto castedComponent = qobject_cast<Qt3DRender::QMaterial *>(object: component)) | 
| 717 |             return castedComponent; | 
| 718 |     } | 
| 719 |     return nullptr; | 
| 720 | } | 
| 721 |  | 
| 722 | void tst_gltfPlugins::compareComponents(Qt3DCore::QComponent *c1, Qt3DCore::QComponent *c2) | 
| 723 | { | 
| 724 |     // Make sure component classes are the same and the non-pointer properties are the same | 
| 725 |     QCOMPARE((c1 == nullptr), (c2 == nullptr)); | 
| 726 |     if (c1) { | 
| 727 |         // Transform names are lost in export, as the transform is just part of the node item | 
| 728 |         if (!qobject_cast<Qt3DCore::QTransform *>(object: c1)) | 
| 729 |             QCOMPARE(c1->objectName(), c2->objectName()); | 
| 730 |         QCOMPARE(c1->metaObject()->className(), c2->metaObject()->className()); | 
| 731 |         // Meshes are all imported as generic meshes | 
| 732 |         if (auto mesh1 = qobject_cast<Qt3DRender::QGeometryRenderer *>(object: c1)) { | 
| 733 |             auto mesh2 = qobject_cast<Qt3DRender::QGeometryRenderer *>(object: c2); | 
| 734 |             QVERIFY(mesh2 != nullptr); | 
| 735 |             auto geometry1 = mesh1->geometry(); | 
| 736 |             auto geometry2 = mesh2->geometry(); | 
| 737 |             // Check that attributes match. | 
| 738 |             compareAttributes( | 
| 739 |                         a1: findAttribute(name: Qt3DRender::QAttribute::defaultPositionAttributeName(), | 
| 740 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 741 |                                       geometry: geometry1), | 
| 742 |                         a2: findAttribute(name: Qt3DRender::QAttribute::defaultPositionAttributeName(), | 
| 743 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 744 |                                       geometry: geometry2)); | 
| 745 |             compareAttributes( | 
| 746 |                         a1: findAttribute(name: Qt3DRender::QAttribute::defaultNormalAttributeName(), | 
| 747 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 748 |                                       geometry: geometry1), | 
| 749 |                         a2: findAttribute(name: Qt3DRender::QAttribute::defaultNormalAttributeName(), | 
| 750 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 751 |                                       geometry: geometry2)); | 
| 752 |             compareAttributes( | 
| 753 |                         a1: findAttribute(name: Qt3DRender::QAttribute::defaultTangentAttributeName(), | 
| 754 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 755 |                                       geometry: geometry1), | 
| 756 |                         a2: findAttribute(name: Qt3DRender::QAttribute::defaultTangentAttributeName(), | 
| 757 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 758 |                                       geometry: geometry2)); | 
| 759 |             compareAttributes( | 
| 760 |                         a1: findAttribute(name: Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName(), | 
| 761 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 762 |                                       geometry: geometry1), | 
| 763 |                         a2: findAttribute(name: Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName(), | 
| 764 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 765 |                                       geometry: geometry2)); | 
| 766 |             compareAttributes( | 
| 767 |                         a1: findAttribute(name: Qt3DRender::QAttribute::defaultColorAttributeName(), | 
| 768 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 769 |                                       geometry: geometry1), | 
| 770 |                         a2: findAttribute(name: Qt3DRender::QAttribute::defaultColorAttributeName(), | 
| 771 |                                       type: Qt3DRender::QAttribute::VertexAttribute, | 
| 772 |                                       geometry: geometry2)); | 
| 773 |             compareAttributes( | 
| 774 |                         a1: findAttribute(QStringLiteral("" ), | 
| 775 |                                       type: Qt3DRender::QAttribute::IndexAttribute, | 
| 776 |                                       geometry: geometry1), | 
| 777 |                         a2: findAttribute(QStringLiteral("" ), | 
| 778 |                                       type: Qt3DRender::QAttribute::IndexAttribute, | 
| 779 |                                       geometry: geometry2)); | 
| 780 |         } else { | 
| 781 |             int count = c1->metaObject()->propertyCount(); | 
| 782 |             for (int i = 0; i < count; i++) { | 
| 783 |                 auto property = c1->metaObject()->property(index: i); | 
| 784 |                 auto v1 = c1->property(name: property.name()); | 
| 785 |                 auto v2 = c2->property(name: property.name()); | 
| 786 |                 if (v1.type() == QVariant::Bool) { | 
| 787 |                     QCOMPARE(v1.toBool(), v2.toBool()); | 
| 788 |                 } else if (v1.type() == QVariant::Color) { | 
| 789 |                     QCOMPARE(v1.value<QColor>(), v2.value<QColor>()); | 
| 790 |                 } else if (v1.type() == QVariant::Vector3D) { | 
| 791 |                     QCOMPARE(v1.value<QVector3D>(), v2.value<QVector3D>()); | 
| 792 |                 } else if (v1.type() == QVariant::Matrix4x4) { | 
| 793 |                     QCOMPARE(v1.value<QMatrix4x4>(), v2.value<QMatrix4x4>()); | 
| 794 |                 } else if (v1.canConvert(targetTypeId: QMetaType::Float)) { | 
| 795 |                     QVERIFY(qFuzzyCompare(v1.toFloat(), v2.toFloat())); | 
| 796 |                 } | 
| 797 |             } | 
| 798 |             if (QString::fromLatin1(str: c1->metaObject()->className()) | 
| 799 |                     .endsWith(QStringLiteral("Qt3DRender::QMaterial" ))) { | 
| 800 |                 auto m1 = qobject_cast<Qt3DRender::QMaterial *>(object: c1); | 
| 801 |                 auto m2 = qobject_cast<Qt3DRender::QMaterial *>(object: c2); | 
| 802 |                 QVERIFY(m1); | 
| 803 |                 QVERIFY(m2); | 
| 804 |                 auto e1 = m1->effect(); | 
| 805 |                 auto e2 = m2->effect(); | 
| 806 |                 QVERIFY(e1); | 
| 807 |                 QVERIFY(e2); | 
| 808 |                 QCOMPARE(e1->objectName(), e2->objectName()); | 
| 809 |                 QCOMPARE(e1->techniques().size(), e2->techniques().size()); | 
| 810 |  | 
| 811 |                 compareParameters(params1: m1->parameters(), params2: m2->parameters()); | 
| 812 |                 compareParameters(params1: e1->parameters(), params2: e2->parameters()); | 
| 813 |  | 
| 814 |                 for (auto t1 : e1->techniques()) { | 
| 815 |                     bool techMatch = false; | 
| 816 |                     for (auto t2 : e2->techniques()) { | 
| 817 |                         if (t1->objectName() == t2->objectName()) { | 
| 818 |                             techMatch = true; | 
| 819 |                             compareParameters(params1: t1->parameters(), params2: t2->parameters()); | 
| 820 |                             compareFilterKeys(keys1: t1->filterKeys(), keys2: t2->filterKeys()); | 
| 821 |                             compareRenderPasses(passes1: t1->renderPasses(), passes2: t2->renderPasses()); | 
| 822 |                             QCOMPARE(t1->graphicsApiFilter()->api(), | 
| 823 |                                      t2->graphicsApiFilter()->api()); | 
| 824 |                             QCOMPARE(t1->graphicsApiFilter()->profile(), | 
| 825 |                                      t2->graphicsApiFilter()->profile()); | 
| 826 |                             QCOMPARE(t1->graphicsApiFilter()->minorVersion(), | 
| 827 |                                      t2->graphicsApiFilter()->minorVersion()); | 
| 828 |                             QCOMPARE(t1->graphicsApiFilter()->majorVersion(), | 
| 829 |                                      t2->graphicsApiFilter()->majorVersion()); | 
| 830 |                             QCOMPARE(t1->graphicsApiFilter()->extensions(), | 
| 831 |                                      t2->graphicsApiFilter()->extensions()); | 
| 832 |                             QCOMPARE(t1->graphicsApiFilter()->vendor(), | 
| 833 |                                      t2->graphicsApiFilter()->vendor()); | 
| 834 |                         } | 
| 835 |                     } | 
| 836 |                     QVERIFY(techMatch); | 
| 837 |                 } | 
| 838 |             } | 
| 839 |         } | 
| 840 |     } | 
| 841 | } | 
| 842 |  | 
| 843 | Qt3DRender::QAttribute *tst_gltfPlugins::findAttribute(const QString &name, | 
| 844 |                                                        Qt3DRender::QAttribute::AttributeType type, | 
| 845 |                                                        Qt3DRender::QGeometry *geometry) | 
| 846 | { | 
| 847 |     for (auto att : geometry->attributes()) { | 
| 848 |         if ((type == Qt3DRender::QAttribute::IndexAttribute && type == att->attributeType()) | 
| 849 |                 || name == att->name()) { | 
| 850 |             return att; | 
| 851 |         } | 
| 852 |     } | 
| 853 |     return nullptr; | 
| 854 | } | 
| 855 |  | 
| 856 | void tst_gltfPlugins::compareAttributes(Qt3DRender::QAttribute *a1, Qt3DRender::QAttribute *a2) | 
| 857 | { | 
| 858 |     QCOMPARE(a1 == nullptr, a2 == nullptr); | 
| 859 |     if (a1) { | 
| 860 |         QCOMPARE(a1->attributeType(), a2->attributeType()); | 
| 861 |         QCOMPARE(a1->vertexBaseType(), a2->vertexBaseType()); | 
| 862 |         QCOMPARE(a1->vertexSize(), a2->vertexSize()); | 
| 863 |         QCOMPARE(a1->count(), a2->count()); | 
| 864 |     } | 
| 865 | } | 
| 866 |  | 
| 867 | void tst_gltfPlugins::compareParameters(const QVector<Qt3DRender::QParameter *> ¶ms1, | 
| 868 |                                         const QVector<Qt3DRender::QParameter *> ¶ms2) | 
| 869 | { | 
| 870 |     QCOMPARE(params1.size(), params2.size()); | 
| 871 |     for (auto p1 : params1) { | 
| 872 |         bool pMatch = false; | 
| 873 |         for (auto p2 : params2) { | 
| 874 |             if (p1->name() == p2->name()) { | 
| 875 |                 pMatch = true; | 
| 876 |                 if (p1->value().type() == QVariant::Color) { | 
| 877 |                     // Colors are imported as QVector4Ds | 
| 878 |                     QColor color = p1->value().value<QColor>(); | 
| 879 |                     QVector4D vec = p2->value().value<QVector4D>(); | 
| 880 |                     QCOMPARE(color.redF(), vec.x()); | 
| 881 |                     QCOMPARE(color.greenF(), vec.y()); | 
| 882 |                     QCOMPARE(color.blueF(), vec.z()); | 
| 883 |                     QCOMPARE(color.alphaF(), vec.w()); | 
| 884 |                 } else if (p1->value().canConvert<Qt3DRender::QAbstractTexture *>()) { | 
| 885 |                     QUrl u1 = getTextureUrl(tex: p1->value().value<Qt3DRender::QAbstractTexture *>()); | 
| 886 |                     QUrl u2 = getTextureUrl(tex: p2->value().value<Qt3DRender::QAbstractTexture *>()); | 
| 887 |                     QCOMPARE(u1.fileName(), u2.fileName()); | 
| 888 |                 } else { | 
| 889 |                     QCOMPARE(p1->value(), p2->value()); | 
| 890 |                 } | 
| 891 |             } | 
| 892 |         } | 
| 893 |         QVERIFY(pMatch); | 
| 894 |     } | 
| 895 | } | 
| 896 |  | 
| 897 | void tst_gltfPlugins::compareRenderPasses(const QVector<Qt3DRender::QRenderPass *> &passes1, | 
| 898 |                                           const QVector<Qt3DRender::QRenderPass *> &passes2) | 
| 899 | { | 
| 900 |     QCOMPARE(passes1.size(), passes2.size()); | 
| 901 |     for (auto pass1 : passes1) { | 
| 902 |         bool passMatch = false; | 
| 903 |         for (auto pass2 : passes2) { | 
| 904 |             if (pass1->objectName() == pass2->objectName()) { | 
| 905 |                 passMatch = true; | 
| 906 |                 compareFilterKeys(keys1: pass1->filterKeys(), keys2: pass2->filterKeys()); | 
| 907 |                 compareParameters(params1: pass1->parameters(), params2: pass2->parameters()); | 
| 908 |  | 
| 909 |                 QVector<Qt3DRender::QRenderState *> states1 = pass1->renderStates(); | 
| 910 |                 QVector<Qt3DRender::QRenderState *> states2 = pass2->renderStates(); | 
| 911 |                 QCOMPARE(states1.size(), states2.size()); | 
| 912 |                 for (auto state1 : states1) { | 
| 913 |                     bool stateMatch = false; | 
| 914 |                     for (auto state2 : states2) { | 
| 915 |                         if (state1->metaObject()->className() | 
| 916 |                                 == state2->metaObject()->className()) { | 
| 917 |                             stateMatch = true; | 
| 918 |                         } | 
| 919 |                     } | 
| 920 |                     QVERIFY(stateMatch); | 
| 921 |                 } | 
| 922 |  | 
| 923 |                 QCOMPARE(pass1->shaderProgram()->vertexShaderCode(), | 
| 924 |                          pass2->shaderProgram()->vertexShaderCode()); | 
| 925 |                 QCOMPARE(pass1->shaderProgram()->fragmentShaderCode(), | 
| 926 |                          pass2->shaderProgram()->fragmentShaderCode()); | 
| 927 |             } | 
| 928 |         } | 
| 929 |         QVERIFY(passMatch); | 
| 930 |     } | 
| 931 | } | 
| 932 |  | 
| 933 | void tst_gltfPlugins::compareFilterKeys(const QVector<Qt3DRender::QFilterKey *> &keys1, | 
| 934 |                                         const QVector<Qt3DRender::QFilterKey *> &keys2) | 
| 935 | { | 
| 936 |     QCOMPARE(keys1.size(), keys2.size()); | 
| 937 |     for (auto k1 : keys1) { | 
| 938 |         bool kMatch = false; | 
| 939 |         for (auto k2 : keys2) { | 
| 940 |             if (k1->name() == k2->name()) { | 
| 941 |                 kMatch = true; | 
| 942 |                 QCOMPARE(k1->value(), k2->value()); | 
| 943 |             } | 
| 944 |         } | 
| 945 |         QVERIFY(kMatch); | 
| 946 |     } | 
| 947 | } | 
| 948 |  | 
| 949 | QUrl tst_gltfPlugins::getTextureUrl(Qt3DRender::QAbstractTexture *tex) | 
| 950 | { | 
| 951 |     QUrl url; | 
| 952 |     if (tex->textureImages().size()) { | 
| 953 |         Qt3DRender::QTextureImage *img = | 
| 954 |                 qobject_cast<Qt3DRender::QTextureImage *>( | 
| 955 |                     object: tex->textureImages().at(i: 0)); | 
| 956 |         if (img) | 
| 957 |             url = img->source(); | 
| 958 |     } | 
| 959 |     return url; | 
| 960 | } | 
| 961 |  | 
| 962 | Qt3DRender::QGeometryRenderer *tst_gltfPlugins::createCustomCube() | 
| 963 | { | 
| 964 |     Qt3DRender::QGeometryRenderer *boxMesh = new Qt3DRender::QGeometryRenderer; | 
| 965 |     Qt3DRender::QGeometry *boxGeometry = new Qt3DRender::QGeometry(boxMesh); | 
| 966 |     Qt3DRender::QBuffer *boxDataBuffer = | 
| 967 |             new Qt3DRender::QBuffer(boxGeometry); | 
| 968 |     Qt3DRender::QBuffer *indexDataBuffer = | 
| 969 |             new Qt3DRender::QBuffer(boxGeometry); | 
| 970 |     QByteArray vertexBufferData; | 
| 971 |     QByteArray indexBufferData; | 
| 972 |  | 
| 973 |     vertexBufferData.resize(size: 8 * 3 * sizeof(float)); | 
| 974 |     indexBufferData.resize(size: 12 * 3 * sizeof(ushort)); | 
| 975 |  | 
| 976 |     float dimension = 1.0f; | 
| 977 |  | 
| 978 |     float *vPtr = reinterpret_cast<float *>(vertexBufferData.data()); | 
| 979 |     vPtr[0]  = -dimension; vPtr[1]  = -dimension; vPtr[2]  = -dimension; | 
| 980 |     vPtr[3]  = dimension;  vPtr[4]  = -dimension; vPtr[5]  = -dimension; | 
| 981 |     vPtr[6]  = dimension;  vPtr[7]  = -dimension; vPtr[8]  = dimension; | 
| 982 |     vPtr[9]  = -dimension; vPtr[10] = -dimension; vPtr[11] = dimension; | 
| 983 |     vPtr[12] = -dimension; vPtr[13] = dimension;  vPtr[14] = -dimension; | 
| 984 |     vPtr[15] = dimension;  vPtr[16] = dimension;  vPtr[17] = -dimension; | 
| 985 |     vPtr[18] = dimension;  vPtr[19] = dimension;  vPtr[20] = dimension; | 
| 986 |     vPtr[21] = -dimension; vPtr[22] = dimension;  vPtr[23] = dimension; | 
| 987 |  | 
| 988 |     ushort *iPtr = reinterpret_cast<ushort *>(indexBufferData.data()); | 
| 989 |     iPtr[0]  = 2; iPtr[1]  = 0; iPtr[2]  = 1; | 
| 990 |     iPtr[3]  = 2; iPtr[4]  = 3; iPtr[5]  = 0; | 
| 991 |     iPtr[6]  = 1; iPtr[7]  = 6; iPtr[8]  = 2; | 
| 992 |     iPtr[9]  = 1; iPtr[10] = 5; iPtr[11] = 6; | 
| 993 |     iPtr[12] = 2; iPtr[13] = 7; iPtr[14] = 3; | 
| 994 |     iPtr[15] = 2; iPtr[16] = 6; iPtr[17] = 7; | 
| 995 |     iPtr[18] = 6; iPtr[19] = 5; iPtr[20] = 4; | 
| 996 |     iPtr[21] = 6; iPtr[22] = 4; iPtr[23] = 7; | 
| 997 |     iPtr[24] = 7; iPtr[25] = 0; iPtr[26] = 3; | 
| 998 |     iPtr[27] = 7; iPtr[28] = 4; iPtr[29] = 0; | 
| 999 |     iPtr[30] = 4; iPtr[31] = 1; iPtr[32] = 0; | 
| 1000 |     iPtr[33] = 4; iPtr[34] = 5; iPtr[35] = 1; | 
| 1001 |  | 
| 1002 |     boxDataBuffer->setData(vertexBufferData); | 
| 1003 |     indexDataBuffer->setData(indexBufferData); | 
| 1004 |  | 
| 1005 |     addPositionAttributeToGeometry(geometry: boxGeometry, buffer: boxDataBuffer, count: 8); | 
| 1006 |     addIndexAttributeToGeometry(geometry: boxGeometry, buffer: indexDataBuffer, count: 36); | 
| 1007 |  | 
| 1008 |     boxMesh->setInstanceCount(1); | 
| 1009 |     boxMesh->setIndexOffset(0); | 
| 1010 |     boxMesh->setFirstInstance(0); | 
| 1011 |     boxMesh->setVertexCount(36); | 
| 1012 |     boxMesh->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles); | 
| 1013 |     boxMesh->setGeometry(boxGeometry); | 
| 1014 |  | 
| 1015 |     return boxMesh; | 
| 1016 | } | 
| 1017 |  | 
| 1018 | Qt3DRender::QEffect *tst_gltfPlugins::createOnTopEffect() | 
| 1019 | { | 
| 1020 |     Qt3DRender::QEffect *effect = new Qt3DRender::QEffect; | 
| 1021 |  | 
| 1022 |     Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique(); | 
| 1023 |     technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile); | 
| 1024 |     technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL); | 
| 1025 |     technique->graphicsApiFilter()->setMajorVersion(2); | 
| 1026 |     technique->graphicsApiFilter()->setMinorVersion(1); | 
| 1027 |  | 
| 1028 |     Qt3DRender::QTechnique *techniqueCore = new Qt3DRender::QTechnique(); | 
| 1029 |     techniqueCore->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile); | 
| 1030 |     techniqueCore->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL); | 
| 1031 |     techniqueCore->graphicsApiFilter()->setMajorVersion(3); | 
| 1032 |     techniqueCore->graphicsApiFilter()->setMinorVersion(1); | 
| 1033 |  | 
| 1034 |     Qt3DRender::QTechnique *techniqueES2 = new Qt3DRender::QTechnique(); | 
| 1035 |     techniqueES2->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES); | 
| 1036 |     techniqueES2->graphicsApiFilter()->setMajorVersion(2); | 
| 1037 |     techniqueES2->graphicsApiFilter()->setMinorVersion(0); | 
| 1038 |     techniqueES2->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile); | 
| 1039 |  | 
| 1040 |     Qt3DRender::QFilterKey *filterkey1 = new Qt3DRender::QFilterKey(effect); | 
| 1041 |     Qt3DRender::QFilterKey *filterkey2 = new Qt3DRender::QFilterKey(); | 
| 1042 |     filterkey1->setName(QStringLiteral("renderingStyle" )); | 
| 1043 |     filterkey1->setValue(QStringLiteral("forward" )); | 
| 1044 |     filterkey2->setName(QStringLiteral("dummyKey" )); | 
| 1045 |     filterkey2->setValue(QStringLiteral("dummyValue" )); | 
| 1046 |  | 
| 1047 |     Qt3DRender::QParameter *parameter1 = new Qt3DRender::QParameter(QStringLiteral("handleColor" ), | 
| 1048 |                                                                     QColor(Qt::yellow)); | 
| 1049 |     Qt3DRender::QParameter *parameter2 = new Qt3DRender::QParameter(QStringLiteral("customAlpha" ), | 
| 1050 |                                                                     1.0f); | 
| 1051 |     Qt3DRender::QParameter *parameter3 = new Qt3DRender::QParameter(QStringLiteral("handleColor" ), | 
| 1052 |                                                                     QColor(Qt::blue)); | 
| 1053 |     Qt3DRender::QTexture2D *texture = new Qt3DRender::QTexture2D; | 
| 1054 |     Qt3DRender::QParameter *parameter4 = | 
| 1055 |             new Qt3DRender::QParameter(QStringLiteral("customTexture" ), texture); | 
| 1056 |     Qt3DRender::QTextureImage *ti = new Qt3DRender::QTextureImage(); | 
| 1057 |     parameter4->value().value<Qt3DRender::QAbstractTexture *>()->addTextureImage(textureImage: ti); | 
| 1058 |     ti->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png" ))); | 
| 1059 |  | 
| 1060 |     technique->addFilterKey(filterKey: filterkey1); | 
| 1061 |     technique->addFilterKey(filterKey: filterkey2); | 
| 1062 |     techniqueES2->addFilterKey(filterKey: filterkey1); | 
| 1063 |     techniqueES2->addFilterKey(filterKey: filterkey2); | 
| 1064 |     techniqueCore->addFilterKey(filterKey: filterkey1); | 
| 1065 |  | 
| 1066 |     technique->addParameter(p: parameter1); | 
| 1067 |     technique->addParameter(p: parameter2); | 
| 1068 |     technique->addParameter(p: parameter4); | 
| 1069 |     techniqueES2->addParameter(p: parameter1); | 
| 1070 |     techniqueES2->addParameter(p: parameter2); | 
| 1071 |  | 
| 1072 |     Qt3DRender::QShaderProgram *shader = new Qt3DRender::QShaderProgram(); | 
| 1073 |     Qt3DRender::QShaderProgram *shaderES2 = new Qt3DRender::QShaderProgram(); | 
| 1074 |     shader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource( | 
| 1075 |                                     sourceUrl: QUrl(QStringLiteral("qrc:/ontopmaterial.vert" )))); | 
| 1076 |     shader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource( | 
| 1077 |                                       sourceUrl: QUrl(QStringLiteral("qrc:/ontopmaterial.frag" )))); | 
| 1078 |     shaderES2->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource( | 
| 1079 |                                        sourceUrl: QUrl(QStringLiteral("qrc:/ontopmaterialES2.vert" )))); | 
| 1080 |     shaderES2->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource( | 
| 1081 |                                          sourceUrl: QUrl(QStringLiteral("qrc:/ontopmaterialES2.frag" )))); | 
| 1082 |     shader->setObjectName(QStringLiteral("Basic shader" )); | 
| 1083 |     shaderES2->setObjectName(QStringLiteral("ES2 shader" )); | 
| 1084 |  | 
| 1085 |     Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass(); | 
| 1086 |     Qt3DRender::QRenderPass *renderPassES2 = new Qt3DRender::QRenderPass(); | 
| 1087 |     renderPass->setShaderProgram(shader); | 
| 1088 |     renderPassES2->setShaderProgram(shaderES2); | 
| 1089 |     renderPass->addFilterKey(filterKey: filterkey2); | 
| 1090 |     renderPass->addParameter(p: parameter3); | 
| 1091 |     Qt3DRender::QColorMask *cmask = new Qt3DRender::QColorMask; | 
| 1092 |     cmask->setRedMasked(false); | 
| 1093 |     renderPass->addRenderState(state: cmask); | 
| 1094 |     Qt3DRender::QBlendEquation *be = new Qt3DRender::QBlendEquation; | 
| 1095 |     be->setBlendFunction(Qt3DRender::QBlendEquation::Subtract); | 
| 1096 |     renderPass->addRenderState(state: be); | 
| 1097 |     technique->addRenderPass(pass: renderPassES2); | 
| 1098 |     techniqueES2->addRenderPass(pass: renderPassES2); | 
| 1099 |     techniqueCore->addRenderPass(pass: renderPass); | 
| 1100 |     technique->setObjectName(QStringLiteral("Basic technique" )); | 
| 1101 |     techniqueES2->setObjectName(QStringLiteral("ES2 technique" )); | 
| 1102 |     techniqueCore->setObjectName(QStringLiteral("Core technique" )); | 
| 1103 |     renderPass->setObjectName(QStringLiteral("Basic pass" )); | 
| 1104 |     renderPassES2->setObjectName(QStringLiteral("ES2 pass" )); | 
| 1105 |  | 
| 1106 |     effect->addTechnique(t: technique); | 
| 1107 |     effect->addTechnique(t: techniqueES2); | 
| 1108 |     effect->addTechnique(t: techniqueCore); | 
| 1109 |     effect->setObjectName(QStringLiteral("OnTopEffect" )); | 
| 1110 |  | 
| 1111 |     return effect; | 
| 1112 | } | 
| 1113 |  | 
| 1114 | Qt3DCore::QEntity *tst_gltfPlugins::findCameraChild(Qt3DCore::QEntity *entity, | 
| 1115 |                                                     Qt3DRender::QCameraLens::ProjectionType type) | 
| 1116 | { | 
| 1117 |     for (auto child : entity->children()) { | 
| 1118 |         if (auto childEntity = qobject_cast<Qt3DCore::QEntity *>(object: child)) { | 
| 1119 |             for (auto component : childEntity->components()) { | 
| 1120 |                 if (auto cameraLens = qobject_cast<Qt3DRender::QCameraLens *>(object: component)) { | 
| 1121 |                     if (cameraLens->projectionType() == type) | 
| 1122 |                         return childEntity; | 
| 1123 |                 } | 
| 1124 |             } | 
| 1125 |             if (auto cameraEntity = findCameraChild(entity: childEntity, type)) | 
| 1126 |                 return cameraEntity; | 
| 1127 |         } | 
| 1128 |     } | 
| 1129 |     return nullptr; | 
| 1130 | } | 
| 1131 |  | 
| 1132 | void tst_gltfPlugins::exportAndImport_data() | 
| 1133 | { | 
| 1134 |     QTest::addColumn<bool>(name: "binaryJson" ); | 
| 1135 |     QTest::addColumn<bool>(name: "compactJson" ); | 
| 1136 |  | 
| 1137 |     QTest::newRow(dataTag: "No options" ) << false << false; | 
| 1138 | #ifndef VISUAL_CHECK | 
| 1139 |     QTest::newRow(dataTag: "Binary json" ) << true << false; | 
| 1140 |     QTest::newRow(dataTag: "Compact json" ) << false << true; | 
| 1141 |     QTest::newRow(dataTag: "Binary/Compact json" ) << true << true; // Compact is ignored in this case | 
| 1142 | #endif | 
| 1143 | } | 
| 1144 |  | 
| 1145 | void tst_gltfPlugins::exportAndImport() | 
| 1146 | { | 
| 1147 |     QFETCH(bool, binaryJson); | 
| 1148 |     QFETCH(bool, compactJson); | 
| 1149 |  | 
| 1150 |     createTestScene(); | 
| 1151 |  | 
| 1152 | #ifdef PRESERVE_EXPORT | 
| 1153 |     m_exportDir->setAutoRemove(false); | 
| 1154 |     qDebug() << "Export Directory:"  << m_exportDir->path(); | 
| 1155 | #endif | 
| 1156 |  | 
| 1157 |     const QString sceneName = QStringLiteral("MyGLTFScene" ); | 
| 1158 |     const QString exportDir = m_exportDir->path(); | 
| 1159 |  | 
| 1160 |     // Export the created scene using GLTF export plugin | 
| 1161 |     QStringList keys = Qt3DRender::QSceneExportFactory::keys(); | 
| 1162 |     for (const QString &key : keys) { | 
| 1163 |         Qt3DRender::QSceneExporter *exporter = | 
| 1164 |                 Qt3DRender::QSceneExportFactory::create(name: key, args: QStringList()); | 
| 1165 |         if (exporter != nullptr && key == QStringLiteral("gltfexport" )) { | 
| 1166 |             QVariantHash options; | 
| 1167 |             options.insert(QStringLiteral("binaryJson" ), avalue: QVariant(binaryJson)); | 
| 1168 |             options.insert(QStringLiteral("compactJson" ), avalue: QVariant(compactJson)); | 
| 1169 |             exporter->exportScene(sceneRoot: m_sceneRoot1, outDir: exportDir, exportName: sceneName, options); | 
| 1170 |             break; | 
| 1171 |         } | 
| 1172 |     } | 
| 1173 |  | 
| 1174 |     QCoreApplication::processEvents(); | 
| 1175 |  | 
| 1176 |     // Import the exported scene using GLTF import plugin | 
| 1177 |     Qt3DCore::QEntity *importedScene = nullptr; | 
| 1178 |     keys = Qt3DRender::QSceneImportFactory::keys(); | 
| 1179 |     for (auto key : keys) { | 
| 1180 |         Qt3DRender::QSceneImporter *importer = | 
| 1181 |                 Qt3DRender::QSceneImportFactory::create(name: key, args: QStringList()); | 
| 1182 |         if (importer != nullptr && key == QStringLiteral("gltf" )) { | 
| 1183 |             QString sceneSource = exportDir; | 
| 1184 |             if (!sceneSource.endsWith(c: QLatin1Char('/'))) | 
| 1185 |                 sceneSource.append(c: QLatin1Char('/')); | 
| 1186 |             sceneSource += sceneName; | 
| 1187 |             sceneSource += QLatin1Char('/'); | 
| 1188 |             sceneSource += sceneName; | 
| 1189 |             sceneSource += QStringLiteral(".qgltf" ); | 
| 1190 |             importer->setSource(QUrl::fromLocalFile(localfile: sceneSource)); | 
| 1191 |             importedScene = importer->scene(); | 
| 1192 |             break; | 
| 1193 |         } | 
| 1194 |     } | 
| 1195 |  | 
| 1196 |     importedScene->setParent(m_sceneRoot2); | 
| 1197 |  | 
| 1198 |     // Compare contents of the original scene and the exported one. | 
| 1199 |     for (auto it = m_entityMap.begin(), end = m_entityMap.end(); it != end; ++it) { | 
| 1200 |         QString name = it.key(); | 
| 1201 |         Qt3DCore::QEntity *exportedEntity = it.value(); | 
| 1202 |         Qt3DCore::QEntity *importedEntity = findChildEntity(entity: importedScene, name); | 
| 1203 |         QVERIFY(importedEntity != nullptr); | 
| 1204 |         if (importedEntity) { | 
| 1205 |             compareComponents(c1: transformComponent(entity: exportedEntity), | 
| 1206 |                               c2: transformComponent(entity: importedEntity)); | 
| 1207 |             compareComponents(c1: lightComponent(entity: exportedEntity), | 
| 1208 |                               c2: lightComponent(entity: importedEntity)); | 
| 1209 |             compareComponents(c1: cameraComponent(entity: exportedEntity), | 
| 1210 |                               c2: cameraComponent(entity: importedEntity)); | 
| 1211 |             compareComponents(c1: meshComponent(entity: exportedEntity), | 
| 1212 |                               c2: meshComponent(entity: importedEntity)); | 
| 1213 |             compareComponents(c1: materialComponent(entity: exportedEntity), | 
| 1214 |                               c2: materialComponent(entity: importedEntity)); | 
| 1215 |             Qt3DRender::QCamera *exportedCamera = | 
| 1216 |                     qobject_cast<Qt3DRender::QCamera *>(object: exportedEntity); | 
| 1217 |             if (exportedCamera) { | 
| 1218 |                 Qt3DRender::QCamera *importedCamera = | 
| 1219 |                         qobject_cast<Qt3DRender::QCamera *>(object: importedEntity); | 
| 1220 |                 QVERIFY(importedCamera != nullptr); | 
| 1221 |                 QCOMPARE(exportedCamera->position(), importedCamera->position()); | 
| 1222 |                 QCOMPARE(exportedCamera->upVector(), importedCamera->upVector()); | 
| 1223 |                 QCOMPARE(exportedCamera->viewCenter(), importedCamera->viewCenter()); | 
| 1224 |             } | 
| 1225 |         } | 
| 1226 |     } | 
| 1227 |  | 
| 1228 |  | 
| 1229 | #ifdef VISUAL_CHECK | 
| 1230 |     qDebug() << "Dumping original entity tree:" ; | 
| 1231 |     walkEntity(m_sceneRoot1, 0); | 
| 1232 |     qDebug() << "Dumping imported entity tree:" ; | 
| 1233 |     walkEntity(importedScene, 0); | 
| 1234 |  | 
| 1235 |     // Find the camera to actually show the scene | 
| 1236 |     m_view2->defaultFrameGraph()->setCamera( | 
| 1237 |                 findCameraChild(m_sceneRoot2, Qt3DRender::QCameraLens::OrthographicProjection)); | 
| 1238 |     QTest::qWait(VISUAL_CHECK); | 
| 1239 |  | 
| 1240 |     m_view1->defaultFrameGraph()->setCamera( | 
| 1241 |                 findCameraChild(m_sceneRoot1, Qt3DRender::QCameraLens::PerspectiveProjection)); | 
| 1242 |     m_view2->defaultFrameGraph()->setCamera( | 
| 1243 |                 findCameraChild(m_sceneRoot2, Qt3DRender::QCameraLens::PerspectiveProjection)); | 
| 1244 |     QTest::qWait(VISUAL_CHECK); | 
| 1245 | #endif | 
| 1246 | } | 
| 1247 |  | 
| 1248 | QTEST_MAIN(tst_gltfPlugins) | 
| 1249 |  | 
| 1250 | #include "tst_gltfplugins.moc" | 
| 1251 |  |