| 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 | |