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
92class tst_gltfPlugins : public QObject
93{
94 Q_OBJECT
95
96private Q_SLOTS:
97
98 void initTestCase();
99 void init();
100 void cleanup();
101 void exportAndImport_data();
102 void exportAndImport();
103
104private:
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 *> &params1,
132 const QVector<Qt3DRender::QParameter *> &params2);
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
151void 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
160void 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
171void 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
186void 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
197void 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
222void 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
617void 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
633void 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
648void 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
664Qt3DCore::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
677Qt3DCore::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
686Qt3DRender::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
695Qt3DRender::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
704Qt3DRender::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
713Qt3DRender::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
722void 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
843Qt3DRender::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
856void 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
867void tst_gltfPlugins::compareParameters(const QVector<Qt3DRender::QParameter *> &params1,
868 const QVector<Qt3DRender::QParameter *> &params2)
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
897void 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
933void 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
949QUrl 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
962Qt3DRender::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
1018Qt3DRender::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
1114Qt3DCore::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
1132void 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
1145void 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
1248QTEST_MAIN(tst_gltfPlugins)
1249
1250#include "tst_gltfplugins.moc"
1251

source code of qt3d/tests/auto/render/gltfplugins/tst_gltfplugins.cpp