1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
2// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "gltfimporter.h"
6
7#include <QtCore/qdir.h>
8#include <QtCore/qfileinfo.h>
9#include <QtCore/qjsonarray.h>
10#include <QtCore/qjsonobject.h>
11#include <QtCore/qmath.h>
12
13#include <QtGui/qvector2d.h>
14
15#include <Qt3DCore/qentity.h>
16#include <Qt3DCore/qtransform.h>
17#include <Qt3DCore/qgeometry.h>
18
19#include <Qt3DRender/qcameralens.h>
20#include <Qt3DRender/qcamera.h>
21#include <Qt3DRender/qalphacoverage.h>
22#include <Qt3DRender/qalphatest.h>
23#include <Qt3DRender/qblendequation.h>
24#include <Qt3DRender/qblendequationarguments.h>
25#include <Qt3DRender/qclipplane.h>
26#include <Qt3DRender/qcolormask.h>
27#include <Qt3DRender/qcullface.h>
28#include <Qt3DRender/qdithering.h>
29#include <Qt3DRender/qmultisampleantialiasing.h>
30#include <Qt3DRender/qpointsize.h>
31#include <Qt3DRender/qnodepthmask.h>
32#include <Qt3DRender/qdepthrange.h>
33#include <Qt3DRender/qdepthtest.h>
34#include <Qt3DRender/qseamlesscubemap.h>
35#include <Qt3DRender/qstencilmask.h>
36#include <Qt3DRender/qstenciloperation.h>
37#include <Qt3DRender/qstenciloperationarguments.h>
38#include <Qt3DRender/qstenciltest.h>
39#include <Qt3DRender/qstenciltestarguments.h>
40#include <Qt3DRender/qeffect.h>
41#include <Qt3DRender/qfrontface.h>
42#include <Qt3DRender/qgeometryrenderer.h>
43#include <Qt3DRender/qmaterial.h>
44#include <Qt3DRender/qgraphicsapifilter.h>
45#include <Qt3DRender/qparameter.h>
46#include <Qt3DRender/qpolygonoffset.h>
47#include <Qt3DRender/qrenderstate.h>
48#include <Qt3DRender/qscissortest.h>
49#include <Qt3DRender/qshaderprogram.h>
50#include <Qt3DRender/qtechnique.h>
51#include <Qt3DRender/qtexture.h>
52#include <Qt3DRender/qtextureimagedatagenerator.h>
53#include <Qt3DRender/qdirectionallight.h>
54#include <Qt3DRender/qspotlight.h>
55#include <Qt3DRender/qpointlight.h>
56
57#include <Qt3DExtras/qphongmaterial.h>
58#include <Qt3DExtras/qphongalphamaterial.h>
59#include <Qt3DExtras/qdiffusemapmaterial.h>
60#include <Qt3DExtras/qdiffusespecularmapmaterial.h>
61#include <Qt3DExtras/qnormaldiffusemapmaterial.h>
62#include <Qt3DExtras/qnormaldiffusemapalphamaterial.h>
63#include <Qt3DExtras/qnormaldiffusespecularmapmaterial.h>
64#include <Qt3DExtras/qgoochmaterial.h>
65#include <Qt3DExtras/qpervertexcolormaterial.h>
66#include <Qt3DExtras/qmetalroughmaterial.h>
67#include <Qt3DExtras/qconemesh.h>
68#include <Qt3DExtras/qcuboidmesh.h>
69#include <Qt3DExtras/qcylindermesh.h>
70#include <Qt3DExtras/qplanemesh.h>
71#include <Qt3DExtras/qspheremesh.h>
72#include <Qt3DExtras/qtorusmesh.h>
73
74#include <private/qurlhelper_p.h>
75#include <private/qloadgltf_p.h>
76
77/**
78 * glTF 2.0 conformance report
79 *
80 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
81 * Samples: https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0
82 *
83 * Most of the reference samples are rendered correctly, with the following exceptions:
84 *
85 * 'extensions' and 'extras' are ignored everywhere except in nodes.
86 *
87 * asset
88 * generator, copyright, minVersion: not parsed
89 * accessors
90 * min, max, normalized, sparse: not parsed
91 * animations
92 * the whole object is not parsed
93 * buffers
94 * all parsed
95 * bufferViews
96 * all parsed
97 * cameras
98 * all parsed
99 * images
100 * mimeType, bufferView, name: not parsed
101 * materials
102 * emissiveTexture, emissiveFactor: not parsed
103 * alphaMode, alphaCutoff, doubleSided: not parsed
104 * texCoord, strength: not parsed
105 * meshes
106 * weights: not parsed
107 * nodes
108 * skin, weights: not parsed
109 * samplers
110 * all parsed
111 * scenes
112 * all parsed
113 * textures
114 * all parsed
115 */
116
117#ifndef qUtf16PrintableImpl // -Impl is a Qt 5.8 feature
118# define qUtf16PrintableImpl(string) \
119 static_cast<const wchar_t*>(static_cast<const void*>(string.utf16()))
120#endif
121
122#define KEY_ASSET QLatin1String("asset")
123#define KEY_VERSION QLatin1String("version")
124#define KEY_CAMERA QLatin1String("camera")
125#define KEY_CAMERAS QLatin1String("cameras")
126#define KEY_SCENES QLatin1String("scenes")
127#define KEY_NODES QLatin1String("nodes")
128#define KEY_MESHES QLatin1String("meshes")
129#define KEY_CHILDREN QLatin1String("children")
130#define KEY_MESH QLatin1String("mesh")
131#define KEY_MATRIX QLatin1String("matrix")
132#define KEY_ROTATION QLatin1String("rotation")
133#define KEY_SCALE QLatin1String("scale")
134#define KEY_TRANSLATION QLatin1String("translation")
135#define KEY_TYPE QLatin1String("type")
136#define KEY_PERSPECTIVE QLatin1String("perspective")
137#define KEY_ORTHOGRAPHIC QLatin1String("orthographic")
138#define KEY_NAME QLatin1String("name")
139#define KEY_COUNT QLatin1String("count")
140#define KEY_YFOV QLatin1String("yfov")
141#define KEY_ZNEAR QLatin1String("znear")
142#define KEY_ZFAR QLatin1String("zfar")
143#define KEY_XMAG QLatin1String("xmag")
144#define KEY_YMAG QLatin1String("ymag")
145#define KEY_MATERIALS QLatin1String("materials")
146#define KEY_EXTENSIONS QLatin1String("extensions")
147#define KEY_COMMON_MAT QLatin1String("KHR_materials_common")
148#define KEY_TECHNIQUE QLatin1String("technique")
149#define KEY_VALUES QLatin1String("values")
150#define KEY_BUFFERS QLatin1String("buffers")
151#define KEY_SHADERS QLatin1String("shaders")
152#define KEY_PROGRAMS QLatin1String("programs")
153#define KEY_PROGRAM QLatin1String("program")
154#define KEY_TECHNIQUES QLatin1String("techniques")
155#define KEY_ACCESSORS QLatin1String("accessors")
156#define KEY_IMAGES QLatin1String("images")
157#define KEY_TEXTURES QLatin1String("textures")
158#define KEY_SCENE QLatin1String("scene")
159#define KEY_BUFFER QLatin1String("buffer")
160#define KEY_TARGET QLatin1String("target")
161#define KEY_BYTE_OFFSET QLatin1String("byteOffset")
162#define KEY_BYTE_LENGTH QLatin1String("byteLength")
163#define KEY_BYTE_STRIDE QLatin1String("byteStride")
164#define KEY_PRIMITIVES QLatin1String("primitives")
165#define KEY_MODE QLatin1String("mode")
166#define KEY_MATERIAL QLatin1String("material")
167#define KEY_ATTRIBUTES QLatin1String("attributes")
168#define KEY_INDICES QLatin1String("indices")
169#define KEY_URI QLatin1String("uri")
170#define KEY_FORMAT QLatin1String("format")
171#define KEY_PASSES QLatin1String("passes")
172#define KEY_SOURCE QLatin1String("source")
173#define KEY_SAMPLER QLatin1String("sampler")
174#define KEY_SAMPLERS QLatin1String("samplers")
175#define KEY_SEMANTIC QLatin1String("semantic")
176#define KEY_STATES QLatin1String("states")
177#define KEY_UNIFORMS QLatin1String("uniforms")
178#define KEY_PARAMETERS QLatin1String("parameters")
179#define KEY_WRAP_S QLatin1String("wrapS")
180#define KEY_MIN_FILTER QLatin1String("minFilter")
181#define KEY_MAG_FILTER QLatin1String("magFilter")
182#define KEY_LIGHT QLatin1String("light")
183#define KEY_LIGHTS QLatin1String("lights")
184#define KEY_POINT_LIGHT QLatin1String("point")
185#define KEY_DIRECTIONAL_LIGHT QLatin1String("directional")
186#define KEY_SPOT_LIGHT QLatin1String("spot")
187#define KEY_AMBIENT_LIGHT QLatin1String("ambient")
188#define KEY_COLOR QLatin1String("color")
189#define KEY_FALLOFF_ANGLE QLatin1String("falloffAngle")
190#define KEY_DIRECTION QLatin1String("direction")
191#define KEY_CONST_ATTENUATION QLatin1String("constantAttenuation")
192#define KEY_LINEAR_ATTENUATION QLatin1String("linearAttenuation")
193#define KEY_QUAD_ATTENUATION QLatin1String("quadraticAttenuation")
194#define KEY_INTENSITY QLatin1String("intensity")
195#define KEY_PBR_METAL_ROUGH QLatin1String("pbrMetallicRoughness")
196#define KEY_BASE_COLOR QLatin1String("baseColorFactor")
197#define KEY_BASE_COLOR_TEX QLatin1String("baseColorTexture")
198#define KEY_METAL_FACTOR QLatin1String("metallicFactor")
199#define KEY_METAL_ROUGH_TEX QLatin1String("metallicRoughnessTexture")
200#define KEY_ROUGH_FACTOR QLatin1String("roughnessFactor")
201#define KEY_NORMAL_TEX QLatin1String("normalTexture")
202#define KEY_OCCLUSION_TEX QLatin1String("occlusionTexture")
203#define KEY_INDEX QLatin1String("index")
204
205#define KEY_INSTANCE_TECHNIQUE QLatin1String("instanceTechnique")
206#define KEY_INSTANCE_PROGRAM QLatin1String("instanceProgram")
207#define KEY_BUFFER_VIEWS QLatin1String("bufferViews")
208#define KEY_BUFFER_VIEW QLatin1String("bufferView")
209#define KEY_VERTEX_SHADER QLatin1String("vertexShader")
210#define KEY_FRAGMENT_SHADER QLatin1String("fragmentShader")
211#define KEY_TESS_CTRL_SHADER QLatin1String("tessCtrlShader")
212#define KEY_TESS_EVAL_SHADER QLatin1String("tessEvalShader")
213#define KEY_GEOMETRY_SHADER QLatin1String("geometryShader")
214#define KEY_COMPUTE_SHADER QLatin1String("computeShader")
215#define KEY_INTERNAL_FORMAT QLatin1String("internalFormat")
216#define KEY_COMPONENT_TYPE QLatin1String("componentType")
217#define KEY_ASPECT_RATIO QLatin1String("aspect_ratio")
218#define KEY_VALUE QLatin1String("value")
219#define KEY_ENABLE QLatin1String("enable")
220#define KEY_FUNCTIONS QLatin1String("functions")
221#define KEY_BLEND_EQUATION QLatin1String("blendEquationSeparate")
222#define KEY_BLEND_FUNCTION QLatin1String("blendFuncSeparate")
223#define KEY_TECHNIQUE_CORE QLatin1String("techniqueCore")
224#define KEY_TECHNIQUE_GL2 QLatin1String("techniqueGL2")
225#define KEY_GABIFILTER QLatin1String("gapifilter")
226#define KEY_API QLatin1String("api")
227#define KEY_MAJORVERSION QLatin1String("majorVersion")
228#define KEY_MINORVERSION QLatin1String("minorVersion")
229#define KEY_PROFILE QLatin1String("profile")
230#define KEY_VENDOR QLatin1String("vendor")
231#define KEY_FILTERKEYS QLatin1String("filterkeys")
232#define KEY_RENDERPASSES QLatin1String("renderpasses")
233#define KEY_EFFECT QLatin1String("effect")
234#define KEY_EFFECTS QLatin1String("effects")
235#define KEY_PROPERTIES QLatin1String("properties")
236#define KEY_POSITION QLatin1String("position")
237#define KEY_UPVECTOR QLatin1String("upVector")
238#define KEY_VIEW_CENTER QLatin1String("viewCenter")
239
240QT_BEGIN_NAMESPACE
241
242
243namespace {
244
245inline QVector3D jsonArrToVec3(const QJsonArray &array)
246{
247 return QVector3D(array[0].toDouble(), array[1].toDouble(), array[2].toDouble());
248}
249
250inline QVector4D jsonArrToVec4(const QJsonArray &array)
251{
252 return QVector4D(array[0].toDouble(), array[1].toDouble(),
253 array[2].toDouble(), array[3].toDouble());
254}
255
256inline QVariant jsonArrToColorVariant(const QJsonArray &array)
257{
258 return QVariant(QColor::fromRgbF(r: array[0].toDouble(), g: array[1].toDouble(),
259 b: array[2].toDouble(), a: array[3].toDouble()));
260}
261
262inline QColor vec4ToQColor(const QVariant &vec4Var)
263{
264 const QVector4D v = vec4Var.value<QVector4D>();
265 return QColor::fromRgbF(r: v.x(), g: v.y(), b: v.z());
266}
267
268inline QVariant vec4ToColorVariant(const QVariant &vec4Var)
269{
270 return QVariant(vec4ToQColor(vec4Var));
271}
272
273Qt3DRender::QFilterKey *buildFilterKey(const QString &key, const QJsonValue &val)
274{
275 Qt3DRender::QFilterKey *fk = new Qt3DRender::QFilterKey;
276 fk->setName(key);
277 if (val.isString())
278 fk->setValue(val.toString());
279 else
280 fk->setValue(val.toInt());
281 return fk;
282}
283
284} // namespace
285
286namespace Qt3DRender {
287
288using namespace Qt3DCore;
289using namespace Qt3DExtras;
290
291Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport", QtWarningMsg);
292
293class GLTFRawTextureImage : public QAbstractTextureImage
294{
295 Q_OBJECT
296public:
297 explicit GLTFRawTextureImage(QNode *parent = nullptr);
298
299 QTextureImageDataGeneratorPtr dataGenerator() const final;
300
301 void setImage(const QImage &image);
302
303private:
304 QImage m_image;
305
306 class GLTFRawTextureImageFunctor : public QTextureImageDataGenerator
307 {
308 public:
309 explicit GLTFRawTextureImageFunctor(const QImage &image);
310
311 QTextureImageDataPtr operator()() final;
312 bool operator ==(const QTextureImageDataGenerator &other) const final;
313
314 QT3D_FUNCTOR(GLTFRawTextureImageFunctor)
315 private:
316 QImage m_image;
317 };
318};
319
320GLTFImporter::GLTFImporter()
321 : QSceneImporter()
322 , m_parseDone(false)
323 , m_majorVersion(1)
324 , m_minorVersion(0)
325{
326}
327
328GLTFImporter::~GLTFImporter()
329{
330
331}
332
333/*!
334 \class Qt3DRender::GLTFImporter
335 \inmodule Qt3DRender
336 \internal
337 \brief Handles importing of gltf files.
338*/
339/*!
340 Set the base \a path for importing scenes.
341*/
342void GLTFImporter::setBasePath(const QString& path)
343{
344 m_basePath = path;
345}
346
347/*!
348 Set a \a json document as the file used for importing a scene.
349 Returns true if the operation is successful.
350*/
351bool GLTFImporter::setJSON(const QJsonDocument &json)
352{
353 if (!json.isObject()) {
354 return false;
355 }
356
357 m_json = json;
358 m_parseDone = false;
359
360 return true;
361}
362
363/*!
364 * Sets the path based on parameter \a source. The path is
365 * used by the parser to load the scene file.
366 * If the file is valid, parsing is automatically triggered.
367 */
368void GLTFImporter::setSource(const QUrl &source)
369{
370 const QString path = QUrlHelper::urlToLocalFileOrQrc(url: source);
371 QFileInfo finfo(path);
372 if (Q_UNLIKELY(!finfo.exists())) {
373 qCWarning(GLTFImporterLog, "missing file: %ls", qUtf16PrintableImpl(path));
374 return;
375 }
376 QFile f(path);
377 f.open(flags: QIODevice::ReadOnly);
378
379 if (Q_UNLIKELY(!setJSON(qLoadGLTF(f.readAll())))) {
380 qCWarning(GLTFImporterLog, "not a JSON document");
381 return;
382 }
383
384 setBasePath(finfo.dir().absolutePath());
385}
386
387/*!
388 * Sets the \a basePath used by the parser to load the scene file.
389 * If the file derived from \a data is valid, parsing is automatically
390 * triggered.
391 */
392void GLTFImporter::setData(const QByteArray& data, const QString &basePath)
393{
394 if (Q_UNLIKELY(!setJSON(qLoadGLTF(data)))) {
395 qCWarning(GLTFImporterLog, "not a JSON document");
396 return;
397 }
398
399 setBasePath(basePath);
400}
401
402/*!
403 * Returns true if the \a extensions are supported by the
404 * GLTF parser.
405 */
406bool GLTFImporter::areFileTypesSupported(const QStringList &extensions) const
407{
408 return GLTFImporter::isGLTFSupported(extensions);
409}
410
411/*!
412 Imports the node specified in \a id from the GLTF file.
413*/
414Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
415{
416 QJsonValue jsonVal;
417
418 if (m_majorVersion > 1) {
419 const QJsonArray nodes = m_json.object().value(KEY_NODES).toArray();
420 if (Q_UNLIKELY(id.toInt() >= nodes.count())) {
421 qCWarning(GLTFImporterLog, "unknown node %ls in GLTF file %ls",
422 qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
423 return nullptr;
424 }
425 jsonVal = nodes[id.toInt()];
426 } else {
427 const QJsonObject nodes = m_json.object().value(KEY_NODES).toObject();
428 jsonVal = nodes.value(key: id);
429 if (Q_UNLIKELY(jsonVal.isUndefined())) {
430 qCWarning(GLTFImporterLog, "unknown node %ls in GLTF file %ls",
431 qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
432 return nullptr;
433 }
434 }
435
436 const QJsonObject jsonObj = jsonVal.toObject();
437 QEntity* result = nullptr;
438
439 // Qt3D has a limitation that a QEntity can only have 1 mesh and 1 material component
440 // So if the node has only 1 mesh, we only create 1 QEntity
441 // Otherwise if there are n meshes, there is 1 QEntity, with n children for each mesh/material combo
442 {
443 QList<QEntity *> entities;
444 const QJsonValue meshesValue = jsonObj.value(KEY_MESHES);
445
446 if (meshesValue.isUndefined()) {
447 const QJsonValue mesh = jsonObj.value(KEY_MESH);
448 if (!mesh.isUndefined()) {
449 const QString meshName = QString::number(mesh.toInt());
450 const auto geometryRenderers = std::as_const(t&: m_meshDict).equal_range(key: meshName);
451 for (auto it = geometryRenderers.first; it != geometryRenderers.second; ++it) {
452 QGeometryRenderer *geometryRenderer = it.value();
453 QEntity *entity = new QEntity;
454 entity->addComponent(comp: geometryRenderer);
455 QMaterial *mat = material(id: m_meshMaterialDict[geometryRenderer]);
456 if (mat)
457 entity->addComponent(comp: mat);
458 entities.append(t: entity);
459 }
460 }
461 } else {
462 const auto meshes = meshesValue.toArray();
463 for (const QJsonValue mesh : meshes) {
464 const QString meshName = mesh.toString();
465 const auto geometryRenderers = std::as_const(t&: m_meshDict).equal_range(key: meshName);
466 if (Q_UNLIKELY(geometryRenderers.first == geometryRenderers.second)) {
467 qCWarning(GLTFImporterLog, "node %ls references unknown mesh %ls",
468 qUtf16PrintableImpl(id), qUtf16PrintableImpl(meshName));
469 continue;
470 }
471
472 for (auto it = geometryRenderers.first; it != geometryRenderers.second; ++it) {
473 QGeometryRenderer *geometryRenderer = it.value();
474 QEntity *entity = new QEntity;
475 entity->addComponent(comp: geometryRenderer);
476 QMaterial *mat = material(id: m_meshMaterialDict[geometryRenderer]);
477 if (mat)
478 entity->addComponent(comp: mat);
479 entities.append(t: entity);
480 }
481 }
482 }
483
484 switch (entities.size()) {
485 case 0:
486 break;
487 case 1:
488 result = std::as_const(t&: entities).first();
489 break;
490 default:
491 result = new QEntity;
492 for (QEntity *entity : std::as_const(t&: entities))
493 entity->setParent(result);
494 }
495 }
496
497 const auto cameraVal = jsonObj.value(KEY_CAMERA);
498 const auto matrix = jsonObj.value(KEY_MATRIX);
499 const auto rotation = jsonObj.value(KEY_ROTATION);
500 const auto translation = jsonObj.value(KEY_TRANSLATION);
501 const auto scale = jsonObj.value(KEY_SCALE);
502 Qt3DCore::QTransform *trans = nullptr;
503 QCameraLens *cameraLens = nullptr;
504 QCamera *cameraEntity = nullptr;
505
506 // If the node contains no meshes, results will still be null here.
507 // If the node has camera and transform, promote it to QCamera, as that makes it more
508 // convenient to adjust the imported camera in the application.
509 if (result == nullptr) {
510 if (!cameraVal.isUndefined()
511 && (!matrix.isUndefined() || !rotation.isUndefined() || !translation.isUndefined()
512 || !scale.isUndefined())) {
513 cameraEntity = new QCamera;
514 trans = cameraEntity->transform();
515 cameraLens = cameraEntity->lens();
516 result = cameraEntity;
517 } else {
518 result = new QEntity;
519 }
520 }
521
522 // recursively retrieve children
523 const auto children = jsonObj.value(KEY_CHILDREN).toArray();
524 for (const QJsonValue c : children) {
525 QEntity* child = node(id: (m_majorVersion > 1) ? QString::number(c.toInt()) : c.toString());
526 if (!child)
527 continue;
528 child->setParent(result);
529 }
530
531 renameFromJson(json: jsonObj, object: result);
532
533 // Node Transforms
534 if (!matrix.isUndefined()) {
535 QMatrix4x4 m(Qt::Uninitialized);
536
537 QJsonArray matrixValues = matrix.toArray();
538 for (int i = 0; i < 16; ++i) {
539 double v = matrixValues.at(i).toDouble();
540 m(i % 4, i >> 2) = v;
541 }
542
543 if (!trans)
544 trans = new Qt3DCore::QTransform;
545 trans->setMatrix(m);
546 }
547
548 // Rotation quaternion
549 if (!rotation.isUndefined()) {
550 if (!trans)
551 trans = new Qt3DCore::QTransform;
552
553 QQuaternion quaternion(jsonArrToVec4(array: rotation.toArray()));
554 trans->setRotation(quaternion);
555 }
556
557 // Translation
558 if (!translation.isUndefined()) {
559 if (!trans)
560 trans = new Qt3DCore::QTransform;
561 trans->setTranslation(jsonArrToVec3(array: translation.toArray()));
562 }
563
564 // Scale
565 if (!scale.isUndefined()) {
566 if (!trans)
567 trans = new Qt3DCore::QTransform;
568 trans->setScale3D(jsonArrToVec3(array: scale.toArray()));
569 }
570
571 // Add the Transform component
572 if (trans != nullptr)
573 result->addComponent(comp: trans);
574
575 if (!cameraVal.isUndefined()) {
576 const bool newLens = cameraLens == nullptr;
577 if (newLens)
578 cameraLens = new QCameraLens;
579 const QString cameraID = (m_majorVersion > 1) ? QString::number(cameraVal.toInt()) : cameraVal.toString();
580 if (!fillCamera(lens&: *cameraLens, cameraEntity, id: cameraID)) {
581 qCWarning(GLTFImporterLog, "failed to build camera: %ls on node %ls",
582 qUtf16PrintableImpl(cameraID), qUtf16PrintableImpl(id));
583 } else if (newLens) {
584 result->addComponent(comp: cameraLens);
585 }
586 }
587
588 const auto extensionsVal = jsonObj.value(KEY_EXTENSIONS);
589 if (!extensionsVal.isUndefined()) {
590 const auto commonMat = extensionsVal.toObject().value(KEY_COMMON_MAT);
591 if (!commonMat.isUndefined()) {
592 const QJsonValue lightVal = commonMat.toObject().value(KEY_LIGHT);
593 const QString lightId = (m_majorVersion > 1) ? QString::number(lightVal.toInt()) : lightVal.toString();
594 QAbstractLight *lightComp = m_lights.value(key: lightId);
595 if (Q_UNLIKELY(!lightComp)) {
596 qCWarning(GLTFImporterLog, "failed to find light: %ls for node %ls",
597 qUtf16PrintableImpl(lightId), qUtf16PrintableImpl(id));
598 } else {
599 result->addComponent(comp: lightComp);
600 }
601 }
602 }
603
604 return result;
605}
606
607/*!
608 Imports the scene specified in parameter \a id.
609*/
610Qt3DCore::QEntity* GLTFImporter::scene(const QString &id)
611{
612 parse();
613
614 QEntity* sceneEntity = nullptr;
615
616 if (m_majorVersion > 1) {
617 const QJsonArray scenes = m_json.object().value(KEY_SCENES).toArray();
618 const auto sceneVal = scenes.first();
619 if (Q_UNLIKELY(sceneVal.isUndefined())) {
620 if (Q_UNLIKELY(!id.isNull()))
621 qCWarning(GLTFImporterLog, "GLTF: no such scene %ls in file %ls",
622 qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
623 return defaultScene();
624 }
625 const QJsonObject sceneObj = sceneVal.toObject();
626 sceneEntity = new QEntity;
627 const auto nodes = sceneObj.value(KEY_NODES).toArray();
628 for (const QJsonValue n : nodes) {
629 QEntity* child = node(id: QString::number(n.toInt()));
630 if (!child)
631 continue;
632 child->setParent(sceneEntity);
633 }
634 } else {
635 const QJsonObject scenes = m_json.object().value(KEY_SCENES).toObject();
636 const auto sceneVal = scenes.value(key: id);
637 if (Q_UNLIKELY(sceneVal.isUndefined())) {
638 if (Q_UNLIKELY(!id.isNull()))
639 qCWarning(GLTFImporterLog, "GLTF: no such scene %ls in file %ls",
640 qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
641 return defaultScene();
642 }
643
644 const QJsonObject sceneObj = sceneVal.toObject();
645 sceneEntity = new QEntity;
646 const auto nodes = sceneObj.value(KEY_NODES).toArray();
647 for (const QJsonValue nnv : nodes) {
648 QString nodeName = nnv.toString();
649 QEntity* child = node(id: nodeName);
650 if (!child)
651 continue;
652 child->setParent(sceneEntity);
653 }
654 }
655
656 cleanup();
657
658 return sceneEntity;
659}
660
661GLTFImporter::BufferData::BufferData()
662 : length(0)
663 , data(nullptr)
664{
665}
666
667GLTFImporter::BufferData::BufferData(const QJsonObject &json)
668 : length(json.value(KEY_BYTE_LENGTH).toInt()),
669 path(json.value(KEY_URI).toString()),
670 data(nullptr)
671{
672}
673
674GLTFImporter::ParameterData::ParameterData() :
675 type(0)
676{
677
678}
679
680GLTFImporter::ParameterData::ParameterData(const QJsonObject &json)
681 : semantic(json.value(KEY_SEMANTIC).toString()),
682 type(json.value(KEY_TYPE).toInt())
683{
684}
685
686GLTFImporter::AccessorData::AccessorData()
687 : type(QAttribute::Float)
688 , dataSize(0)
689 , count(0)
690 , offset(0)
691 , stride(0)
692{
693
694}
695
696GLTFImporter::AccessorData::AccessorData(const QJsonObject &json, int major, int minor)
697 : type(accessorTypeFromJSON(componentType: json.value(KEY_COMPONENT_TYPE).toInt())),
698 dataSize(accessorDataSizeFromJson(type: json.value(KEY_TYPE).toString())),
699 count(json.value(KEY_COUNT).toInt()),
700 offset(0),
701 stride(0)
702{
703 Q_UNUSED(minor);
704
705 if (major > 1) {
706 bufferViewName = QString::number(json.value(KEY_BUFFER_VIEW).toInt());
707 } else {
708 bufferViewName = json.value(KEY_BUFFER_VIEW).toString();
709 }
710
711 const auto byteOffset = json.value(KEY_BYTE_OFFSET);
712 if (!byteOffset.isUndefined())
713 offset = byteOffset.toInt();
714 const auto byteStride = json.value(KEY_BYTE_STRIDE);
715 if (!byteStride.isUndefined())
716 stride = byteStride.toInt();
717}
718
719bool GLTFImporter::isGLTFSupported(const QStringList &extensions)
720{
721 for (auto suffix: std::as_const(t: extensions)) {
722 suffix = suffix.toLower();
723 if (suffix == QLatin1String("json") || suffix == QLatin1String("gltf") || suffix == QLatin1String("qgltf"))
724 return true;
725 }
726 return false;
727}
728
729bool GLTFImporter::isEmbeddedResource(const QString &url)
730{
731 return url.startsWith(s: "data:");
732}
733
734void GLTFImporter::renameFromJson(const QJsonObject &json, QObject * const object)
735{
736 const auto name = json.value(KEY_NAME);
737 if (!name.isUndefined())
738 object->setObjectName(name.toString());
739}
740
741bool GLTFImporter::hasStandardUniformNameFromSemantic(const QString &semantic)
742{
743 //Standard Uniforms
744 if (semantic.isEmpty())
745 return false;
746 switch (semantic.at(i: 0).toLatin1()) {
747 case 'L':
748 // return semantic == QLatin1String("LOCAL");
749 return false;
750 case 'M':
751 return semantic == QLatin1String("MODEL")
752 || semantic == QLatin1String("MODELVIEW")
753 || semantic == QLatin1String("MODELVIEWPROJECTION")
754 || semantic == QLatin1String("MODELINVERSE")
755 || semantic == QLatin1String("MODELVIEWPROJECTIONINVERSE")
756 || semantic == QLatin1String("MODELINVERSETRANSPOSE")
757 || semantic == QLatin1String("MODELVIEWINVERSETRANSPOSE");
758 case 'V':
759 return semantic == QLatin1String("VIEW")
760 || semantic == QLatin1String("VIEWINVERSE")
761 || semantic == QLatin1String("VIEWPORT");
762 case 'P':
763 return semantic == QLatin1String("PROJECTION")
764 || semantic == QLatin1String("PROJECTIONINVERSE");
765 }
766 return false;
767}
768
769QString GLTFImporter::standardAttributeNameFromSemantic(const QString &semantic)
770{
771 //Standard Attributes
772 if (semantic.startsWith(s: QLatin1String("POSITION")))
773 return QAttribute::defaultPositionAttributeName();
774 if (semantic.startsWith(s: QLatin1String("NORMAL")))
775 return QAttribute::defaultNormalAttributeName();
776 if (semantic.startsWith(s: QLatin1String("TEXCOORD")))
777 return QAttribute::defaultTextureCoordinateAttributeName();
778 if (semantic.startsWith(s: QLatin1String("COLOR")))
779 return QAttribute::defaultColorAttributeName();
780 if (semantic.startsWith(s: QLatin1String("TANGENT")))
781 return QAttribute::defaultTangentAttributeName();
782
783// if (semantic.startsWith(QLatin1String("JOINT")));
784// if (semantic.startsWith(QLatin1String("JOINTMATRIX")));
785// if (semantic.startsWith(QLatin1String("WEIGHT")));
786
787 return QString();
788}
789
790QParameter *GLTFImporter::parameterFromTechnique(QTechnique *technique,
791 const QString &parameterName)
792{
793 const QList<QParameter *> parameters = m_techniqueParameters.value(key: technique);
794 for (QParameter *parameter : parameters) {
795 if (parameter->name() == parameterName)
796 return parameter;
797 }
798
799 return nullptr;
800}
801
802Qt3DCore::QEntity* GLTFImporter::defaultScene()
803{
804 if (Q_UNLIKELY(m_defaultScene.isEmpty())) {
805 qCWarning(GLTFImporterLog, "no default scene");
806 return nullptr;
807 }
808
809 return scene(id: m_defaultScene);
810}
811
812QMaterial *GLTFImporter::materialWithCustomShader(const QString &id, const QJsonObject &jsonObj)
813{
814 QString effectName = jsonObj.value(KEY_EFFECT).toString();
815 if (effectName.isEmpty()) {
816 // GLTF custom shader material (with qgltf tool specific customizations)
817
818 // Default ES2 Technique
819 QString techniqueName = jsonObj.value(KEY_TECHNIQUE).toString();
820 const auto it = std::as_const(t&: m_techniques).find(key: techniqueName);
821 if (Q_UNLIKELY(it == m_techniques.cend())) {
822 qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
823 qUtf16PrintableImpl(techniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
824 return nullptr;
825 }
826 QTechnique *technique = *it;
827 technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGLES);
828 technique->graphicsApiFilter()->setMajorVersion(2);
829 technique->graphicsApiFilter()->setMinorVersion(0);
830 technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
831
832 //Optional Core technique
833 QTechnique *coreTechnique = nullptr;
834 QTechnique *gl2Technique = nullptr;
835 QString coreTechniqueName = jsonObj.value(KEY_TECHNIQUE_CORE).toString();
836 if (!coreTechniqueName.isNull()) {
837 const auto it = std::as_const(t&: m_techniques).find(key: coreTechniqueName);
838 if (Q_UNLIKELY(it == m_techniques.cend())) {
839 qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
840 qUtf16PrintableImpl(coreTechniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
841 } else {
842 coreTechnique = it.value();
843 coreTechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
844 coreTechnique->graphicsApiFilter()->setMajorVersion(3);
845 coreTechnique->graphicsApiFilter()->setMinorVersion(1);
846 coreTechnique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile);
847 }
848 }
849 //Optional GL2 technique
850 QString gl2TechniqueName = jsonObj.value(KEY_TECHNIQUE_GL2).toString();
851 if (!gl2TechniqueName.isNull()) {
852 const auto it = std::as_const(t&: m_techniques).find(key: gl2TechniqueName);
853 if (Q_UNLIKELY(it == m_techniques.cend())) {
854 qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
855 qUtf16PrintableImpl(gl2TechniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
856 } else {
857 gl2Technique = it.value();
858 gl2Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
859 gl2Technique->graphicsApiFilter()->setMajorVersion(2);
860 gl2Technique->graphicsApiFilter()->setMinorVersion(0);
861 gl2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
862 }
863 }
864
865 // glTF doesn't deal in effects, but we need a trivial one to wrap
866 // up our techniques
867 // However we need to create a unique effect for each material instead
868 // of caching because QMaterial does not keep up with effects
869 // its not the parent of.
870 QEffect *effect = new QEffect;
871 effect->setObjectName(techniqueName);
872 effect->addTechnique(t: technique);
873 if (coreTechnique != nullptr)
874 effect->addTechnique(t: coreTechnique);
875 if (gl2Technique != nullptr)
876 effect->addTechnique(t: gl2Technique);
877
878 QMaterial *mat = new QMaterial;
879 mat->setEffect(effect);
880
881 renameFromJson(json: jsonObj, object: mat);
882
883 const QJsonObject values = jsonObj.value(KEY_VALUES).toObject();
884 for (auto it = values.begin(), end = values.end(); it != end; ++it) {
885 const QString vName = it.key();
886 QParameter *param = parameterFromTechnique(technique, parameterName: vName);
887
888 if (param == nullptr && coreTechnique != nullptr)
889 param = parameterFromTechnique(technique: coreTechnique, parameterName: vName);
890
891 if (param == nullptr && gl2Technique != nullptr)
892 param = parameterFromTechnique(technique: gl2Technique, parameterName: vName);
893
894 if (Q_UNLIKELY(!param)) {
895 qCWarning(GLTFImporterLog, "unknown parameter: %ls in technique %ls processing material %ls",
896 qUtf16PrintableImpl(vName), qUtf16PrintableImpl(techniqueName), qUtf16PrintableImpl(id));
897 continue;
898 }
899
900 ParameterData paramData = m_parameterDataDict.value(key: param);
901 QVariant var = parameterValueFromJSON(type: paramData.type, value: it.value());
902
903 mat->addParameter(parameter: new QParameter(param->name(), var));
904 } // of material technique-instance values iteration
905
906 return mat;
907 } else {
908 // Qt3D exported QGLTF custom material
909 QMaterial *mat = new QMaterial;
910 renameFromJson(json: jsonObj, object: mat);
911 QEffect *effect = m_effects.value(key: effectName);
912 if (effect) {
913 mat->setEffect(effect);
914 } else {
915 qCWarning(GLTFImporterLog, "Effect %ls missing for material %ls",
916 qUtf16PrintableImpl(effectName), qUtf16PrintableImpl(mat->objectName()));
917 }
918 const QJsonObject params = jsonObj.value(KEY_PARAMETERS).toObject();
919 for (auto it = params.begin(), end = params.end(); it != end; ++it)
920 mat->addParameter(parameter: buildParameter(key: it.key(), paramObj: it.value().toObject()));
921
922 return mat;
923 }
924}
925
926QMaterial *GLTFImporter::commonMaterial(const QJsonObject &jsonObj)
927{
928 const auto jsonExt =
929 jsonObj.value(KEY_EXTENSIONS).toObject().value(KEY_COMMON_MAT).toObject();
930 if (m_majorVersion == 1 && jsonExt.isEmpty())
931 return nullptr;
932
933 QVariantHash params;
934 bool hasDiffuseMap = false;
935 bool hasSpecularMap = false;
936 bool hasNormalMap = false;
937 bool hasAlpha = false;
938
939 if (m_majorVersion > 1) {
940 QMaterial *mat = pbrMaterial(jsonObj);
941
942 if (mat)
943 return mat;
944 }
945
946 const QJsonObject values = jsonExt.value(KEY_VALUES).toObject();
947 for (auto it = values.begin(), end = values.end(); it != end; ++it) {
948 const QString vName = it.key();
949 const QJsonValue val = it.value();
950 QVariant var;
951 QString propertyName = vName;
952 if (vName == QLatin1String("ambient") && val.isArray()) {
953 var = vec4ToColorVariant(vec4Var: parameterValueFromJSON(GL_FLOAT_VEC4, value: val));
954 } else if (vName == QLatin1String("diffuse")) {
955 if (val.isString()) {
956 var = parameterValueFromJSON(GL_SAMPLER_2D, value: val);
957 hasDiffuseMap = true;
958 } else if (val.isArray()) {
959 var = vec4ToColorVariant(vec4Var: parameterValueFromJSON(GL_FLOAT_VEC4, value: val));
960 }
961 } else if (vName == QLatin1String("specular")) {
962 if (val.isString()) {
963 var = parameterValueFromJSON(GL_SAMPLER_2D, value: val);
964 hasSpecularMap = true;
965 } else if (val.isArray()) {
966 var = vec4ToColorVariant(vec4Var: parameterValueFromJSON(GL_FLOAT_VEC4, value: val));
967 }
968 } else if (vName == QLatin1String("cool")) { // Custom Qt3D extension for gooch
969 var = vec4ToColorVariant(vec4Var: parameterValueFromJSON(GL_FLOAT_VEC4, value: val));
970 } else if (vName == QLatin1String("warm")) { // Custom Qt3D extension for gooch
971 var = vec4ToColorVariant(vec4Var: parameterValueFromJSON(GL_FLOAT_VEC4, value: val));
972 } else if (vName == QLatin1String("shininess") && val.isDouble()) {
973 var = parameterValueFromJSON(GL_FLOAT, value: val);
974 } else if (vName == QLatin1String("normalmap") && val.isString()) {
975 var = parameterValueFromJSON(GL_SAMPLER_2D, value: val);
976 propertyName = QStringLiteral("normal");
977 hasNormalMap = true;
978 } else if (vName == QLatin1String("transparency")) {
979 var = parameterValueFromJSON(GL_FLOAT, value: val);
980 propertyName = QStringLiteral("alpha");
981 hasAlpha = true;
982 } else if (vName == QLatin1String("transparent")) {
983 hasAlpha = parameterValueFromJSON(GL_BOOL, value: val).toBool();
984 } else if (vName == QLatin1String("textureScale")) {
985 var = parameterValueFromJSON(GL_FLOAT, value: val);
986 propertyName = QStringLiteral("textureScale");
987 } else if (vName == QLatin1String("alpha")) { // Custom Qt3D extension for gooch
988 var = parameterValueFromJSON(GL_FLOAT, value: val);
989 } else if (vName == QLatin1String("beta")) { // Custom Qt3D extension for gooch
990 var = parameterValueFromJSON(GL_FLOAT, value: val);
991 }
992 if (var.isValid())
993 params[propertyName] = var;
994 }
995
996 const QJsonObject funcValues = jsonExt.value(KEY_FUNCTIONS).toObject();
997 if (!funcValues.isEmpty()) {
998 const QJsonArray fArray = funcValues[KEY_BLEND_FUNCTION].toArray();
999 const QJsonArray eArray = funcValues[KEY_BLEND_EQUATION].toArray();
1000 if (fArray.size() == 4) {
1001 params[QStringLiteral("sourceRgbArg")] = fArray[0].toInt();
1002 params[QStringLiteral("sourceAlphaArg")] = fArray[1].toInt();
1003 params[QStringLiteral("destinationRgbArg")] = fArray[2].toInt();
1004 params[QStringLiteral("destinationAlphaArg")] = fArray[3].toInt();
1005 }
1006 // We get separate values but our QPhongAlphaMaterial only supports single argument,
1007 // so we just use the first one.
1008 if (eArray.size() == 2)
1009 params[QStringLiteral("blendFunctionArg")] = eArray[0].toInt();
1010 }
1011
1012 QMaterial *mat = nullptr;
1013 const QString technique = jsonExt.value(KEY_TECHNIQUE).toString();
1014 if (technique == QStringLiteral("PHONG")) {
1015 if (hasNormalMap) {
1016 if (hasSpecularMap) {
1017 mat = new QNormalDiffuseSpecularMapMaterial;
1018 } else {
1019 if (Q_UNLIKELY(!hasDiffuseMap)) {
1020 qCWarning(GLTFImporterLog, "Common material with normal and specular maps needs a diffuse map as well");
1021 } else {
1022 if (hasAlpha)
1023 mat = new QNormalDiffuseMapAlphaMaterial;
1024 else
1025 mat = new QNormalDiffuseMapMaterial;
1026 }
1027 }
1028 } else {
1029 if (hasSpecularMap) {
1030 if (Q_UNLIKELY(!hasDiffuseMap))
1031 qCWarning(GLTFImporterLog, "Common material with specular map needs a diffuse map as well");
1032 else
1033 mat = new QDiffuseSpecularMapMaterial;
1034 } else if (hasDiffuseMap) {
1035 mat = new QDiffuseMapMaterial;
1036 } else {
1037 if (hasAlpha)
1038 mat = new QPhongAlphaMaterial;
1039 else
1040 mat = new QPhongMaterial;
1041 }
1042 }
1043 } else if (technique == QStringLiteral("GOOCH")) { // Qt3D specific extension
1044 mat = new QGoochMaterial;
1045 } else if (technique == QStringLiteral("PERVERTEX")) { // Qt3D specific extension
1046 mat = new QPerVertexColorMaterial;
1047 }
1048
1049 if (Q_UNLIKELY(!mat)) {
1050 qCWarning(GLTFImporterLog, "Could not find a suitable built-in material for KHR_materials_common");
1051 } else {
1052 for (QVariantHash::const_iterator it = params.constBegin(), itEnd = params.constEnd(); it != itEnd; ++it)
1053 mat->setProperty(name: it.key().toUtf8(), value: it.value());
1054
1055 renameFromJson(json: jsonObj, object: mat);
1056 }
1057
1058 return mat;
1059}
1060
1061QMaterial *GLTFImporter::pbrMaterial(const QJsonObject &jsonObj)
1062{
1063 // check for pbrMetallicRoughness material
1064 QMetalRoughMaterial *mrMaterial = nullptr;
1065 QJsonValue jsonValue = jsonObj.value(KEY_PBR_METAL_ROUGH);
1066
1067 if (!jsonValue.isUndefined()) {
1068 const QJsonObject pbrObj = jsonValue.toObject();
1069 mrMaterial = new QMetalRoughMaterial;
1070 jsonValue = pbrObj.value(KEY_BASE_COLOR);
1071 if (!jsonValue.isUndefined())
1072 mrMaterial->setBaseColor(jsonArrToColorVariant(array: jsonValue.toArray()));
1073
1074 jsonValue = pbrObj.value(KEY_BASE_COLOR_TEX);
1075 if (!jsonValue.isUndefined()) {
1076 const QJsonObject texObj = jsonValue.toObject();
1077 const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
1078 const auto it = m_textures.find(key: textureId);
1079 if (Q_UNLIKELY(it == m_textures.end())) {
1080 qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
1081 } else {
1082 mrMaterial->setBaseColor(QVariant::fromValue(value: it.value()));
1083 }
1084 }
1085
1086 jsonValue = pbrObj.value(KEY_METAL_FACTOR);
1087 if (!jsonValue.isUndefined())
1088 mrMaterial->setMetalness(jsonValue.toVariant());
1089
1090 jsonValue = pbrObj.value(KEY_METAL_ROUGH_TEX);
1091 if (!jsonValue.isUndefined()) {
1092 const QJsonObject texObj = jsonValue.toObject();
1093 const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
1094 const auto it = m_textures.find(key: textureId);
1095 if (Q_UNLIKELY(it == m_textures.end())) {
1096 qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
1097 } else {
1098 // find the texture again
1099 const QJsonArray texArray = m_json.object().value(KEY_TEXTURES).toArray();
1100 const QJsonObject tObj = texArray.at(i: texObj.value(KEY_INDEX).toInt()).toObject();
1101 const QString sourceId = QString::number(tObj.value(KEY_SOURCE).toInt());
1102 QImage image;
1103 if (m_imagePaths.contains(key: sourceId)) {
1104 image.load(fileName: m_imagePaths.value(key: sourceId));
1105 } else if (m_imageData.contains(key: sourceId)) {
1106 image = m_imageData.value(key: sourceId);
1107 } else {
1108 return mrMaterial;
1109 }
1110
1111 // at this point, in image there is data for metalness (on B) and
1112 // roughness (on G) bytes. 2 new textures are created
1113 // to make Qt3D happy, since it samples only on R.
1114
1115 QTexture2D* metalTex = new QTexture2D;
1116 QTexture2D* roughTex = new QTexture2D;
1117 GLTFRawTextureImage* metalImgTex = new GLTFRawTextureImage();
1118 GLTFRawTextureImage* roughImgTex = new GLTFRawTextureImage();
1119 QImage metalness(image.size(), image.format());
1120 QImage roughness(image.size(), image.format());
1121
1122 const uchar *imgData = image.constBits();
1123 const int pixelBytes = image.depth() / 8;
1124 Q_ASSERT_X(pixelBytes < 3, "GLTFImporter::pbrMaterial", "Unsupported texture format");
1125
1126 for (int y = 0; y < image.height(); y++) {
1127 for (int x = 0; x < image.width(); x++) {
1128 metalness.setPixel(x, y, index_or_rgb: qRgb(r: imgData[0], g: imgData[0], b: imgData[0]));
1129 roughness.setPixel(x, y, index_or_rgb: qRgb(r: imgData[1], g: imgData[1], b: imgData[1]));
1130 imgData += pixelBytes;
1131 }
1132 }
1133
1134 metalImgTex->setImage(metalness);
1135 metalTex->addTextureImage(textureImage: metalImgTex);
1136 roughImgTex->setImage(roughness);
1137 roughTex->addTextureImage(textureImage: roughImgTex);
1138
1139 setTextureSamplerInfo(id: "", jsonObj: tObj, tex: metalTex);
1140 setTextureSamplerInfo(id: "", jsonObj: tObj, tex: roughTex);
1141
1142 mrMaterial->setMetalness(QVariant::fromValue(value: metalTex));
1143 mrMaterial->setRoughness(QVariant::fromValue(value: roughTex));
1144 }
1145 }
1146
1147 jsonValue = pbrObj.value(KEY_ROUGH_FACTOR);
1148 if (!jsonValue.isUndefined())
1149 mrMaterial->setRoughness(jsonValue.toVariant());
1150 }
1151
1152 jsonValue = jsonObj.value(KEY_NORMAL_TEX);
1153 if (!jsonValue.isUndefined()) {
1154 const QJsonObject texObj = jsonValue.toObject();
1155 const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
1156 const auto it = m_textures.find(key: textureId);
1157 if (Q_UNLIKELY(it == m_textures.end())) {
1158 qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
1159 } else {
1160 if (mrMaterial)
1161 mrMaterial->setNormal(QVariant::fromValue(value: it.value()));
1162 }
1163 }
1164
1165 jsonValue = jsonObj.value(KEY_OCCLUSION_TEX);
1166 if (!jsonValue.isUndefined()) {
1167 const QJsonObject texObj = jsonValue.toObject();
1168 const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
1169 const auto it = m_textures.find(key: textureId);
1170 if (Q_UNLIKELY(it == m_textures.end())) {
1171 qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
1172 } else {
1173 if (mrMaterial)
1174 mrMaterial->setAmbientOcclusion(QVariant::fromValue(value: it.value()));
1175 }
1176 }
1177
1178 return mrMaterial;
1179}
1180
1181QMaterial* GLTFImporter::material(const QString &id)
1182{
1183 const auto it = std::as_const(t&: m_materialCache).find(key: id);
1184 if (it != m_materialCache.cend())
1185 return it.value();
1186
1187 QJsonValue jsonVal;
1188
1189 if (m_majorVersion > 1) {
1190 const QJsonArray mats = m_json.object().value(KEY_MATERIALS).toArray();
1191 jsonVal = mats.at(i: id.toInt());
1192 } else {
1193 const QJsonObject mats = m_json.object().value(KEY_MATERIALS).toObject();
1194 jsonVal = mats.value(key: id);
1195 }
1196
1197 if (Q_UNLIKELY(jsonVal.isUndefined())) {
1198 qCWarning(GLTFImporterLog, "unknown material %ls in GLTF file %ls",
1199 qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
1200 return nullptr;
1201 }
1202
1203 const QJsonObject jsonObj = jsonVal.toObject();
1204
1205 QMaterial *mat = nullptr;
1206
1207 // Prefer common materials over custom shaders.
1208 mat = commonMaterial(jsonObj);
1209 if (!mat)
1210 mat = materialWithCustomShader(id, jsonObj);
1211
1212 m_materialCache[id] = mat;
1213 return mat;
1214}
1215
1216bool GLTFImporter::fillCamera(QCameraLens &lens, QCamera *cameraEntity, const QString &id) const
1217{
1218 QJsonObject jsonObj;
1219
1220 if (m_majorVersion > 1) {
1221 const QJsonArray camArray = m_json.object().value(KEY_CAMERAS).toArray();
1222 if (id.toInt() >= camArray.count()) {
1223 qCWarning(GLTFImporterLog, "unknown camera %ls in GLTF file %ls",
1224 qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
1225 return false;
1226 }
1227 jsonObj = camArray[id.toInt()].toObject();
1228 } else {
1229 const auto jsonVal = m_json.object().value(KEY_CAMERAS).toObject().value(key: id);
1230 if (Q_UNLIKELY(jsonVal.isUndefined())) {
1231 qCWarning(GLTFImporterLog, "unknown camera %ls in GLTF file %ls",
1232 qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
1233 return false;
1234 }
1235 jsonObj = jsonVal.toObject();
1236 }
1237
1238 QString camTy = jsonObj.value(KEY_TYPE).toString();
1239
1240 if (camTy == QLatin1String("perspective")) {
1241 const auto pVal = jsonObj.value(KEY_PERSPECTIVE);
1242 if (Q_UNLIKELY(pVal.isUndefined())) {
1243 qCWarning(GLTFImporterLog, "camera: %ls missing 'perspective' object",
1244 qUtf16PrintableImpl(id));
1245 return false;
1246 }
1247
1248 const QJsonObject pObj = pVal.toObject();
1249 double aspectRatio = pObj.value(KEY_ASPECT_RATIO).toDouble();
1250 double yfov = pObj.value(KEY_YFOV).toDouble();
1251 double frustumNear = pObj.value(KEY_ZNEAR).toDouble();
1252 double frustumFar = pObj.value(KEY_ZFAR).toDouble();
1253
1254 lens.setPerspectiveProjection(fieldOfView: qRadiansToDegrees(radians: yfov), aspect: aspectRatio, nearPlane: frustumNear,
1255 farPlane: frustumFar);
1256 } else if (camTy == QLatin1String("orthographic")) {
1257 const auto pVal = jsonObj.value(KEY_ORTHOGRAPHIC);
1258 if (Q_UNLIKELY(pVal.isUndefined())) {
1259 qCWarning(GLTFImporterLog, "camera: %ls missing 'orthographic' object",
1260 qUtf16PrintableImpl(id));
1261 return false;
1262 }
1263
1264 const QJsonObject pObj = pVal.toObject();
1265 double xmag = pObj.value(KEY_XMAG).toDouble() / 2.0f;
1266 double ymag = pObj.value(KEY_YMAG).toDouble() / 2.0f;
1267 double frustumNear = pObj.value(KEY_ZNEAR).toDouble();
1268 double frustumFar = pObj.value(KEY_ZFAR).toDouble();
1269
1270 lens.setOrthographicProjection(left: -xmag, right: xmag, bottom: -ymag, top: ymag, nearPlane: frustumNear, farPlane: frustumFar);
1271 } else {
1272 qCWarning(GLTFImporterLog, "camera: %ls has unsupported type: %ls",
1273 qUtf16PrintableImpl(id), qUtf16PrintableImpl(camTy));
1274 return false;
1275 }
1276 if (cameraEntity) {
1277 if (jsonObj.contains(KEY_POSITION))
1278 cameraEntity->setPosition(jsonArrToVec3(array: jsonObj.value(KEY_POSITION).toArray()));
1279 if (jsonObj.contains(KEY_UPVECTOR))
1280 cameraEntity->setUpVector(jsonArrToVec3(array: jsonObj.value(KEY_UPVECTOR).toArray()));
1281 if (jsonObj.contains(KEY_VIEW_CENTER))
1282 cameraEntity->setViewCenter(jsonArrToVec3(array: jsonObj.value(KEY_VIEW_CENTER).toArray()));
1283 }
1284 renameFromJson(json: jsonObj, object: &lens);
1285 return true;
1286}
1287
1288void GLTFImporter::parse()
1289{
1290 if (m_parseDone)
1291 return;
1292
1293 const QJsonValue asset = m_json.object().value(KEY_ASSET);
1294 if (!asset.isUndefined())
1295 processJSONAsset(json: asset.toObject());
1296
1297 if (m_majorVersion > 1) {
1298 parseV2();
1299 } else {
1300 parseV1();
1301 }
1302
1303 m_parseDone = true;
1304}
1305
1306void GLTFImporter::parseV1()
1307{
1308 const QJsonObject buffers = m_json.object().value(KEY_BUFFERS).toObject();
1309 for (auto it = buffers.begin(), end = buffers.end(); it != end; ++it)
1310 processJSONBuffer(id: it.key(), json: it.value().toObject());
1311
1312 const QJsonObject views = m_json.object().value(KEY_BUFFER_VIEWS).toObject();
1313 loadBufferData();
1314 for (auto it = views.begin(), end = views.end(); it != end; ++it)
1315 processJSONBufferView(id: it.key(), json: it.value().toObject());
1316 unloadBufferData();
1317
1318 const QJsonObject shaders = m_json.object().value(KEY_SHADERS).toObject();
1319 for (auto it = shaders.begin(), end = shaders.end(); it != end; ++it)
1320 processJSONShader(id: it.key(), jsonObject: it.value().toObject());
1321
1322 const QJsonObject programs = m_json.object().value(KEY_PROGRAMS).toObject();
1323 for (auto it = programs.begin(), end = programs.end(); it != end; ++it)
1324 processJSONProgram(id: it.key(), jsonObject: it.value().toObject());
1325
1326 const QJsonObject attrs = m_json.object().value(KEY_ACCESSORS).toObject();
1327 for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it)
1328 processJSONAccessor(id: it.key(), json: it.value().toObject());
1329
1330 const QJsonObject meshes = m_json.object().value(KEY_MESHES).toObject();
1331 for (auto it = meshes.begin(), end = meshes.end(); it != end; ++it)
1332 processJSONMesh(id: it.key(), json: it.value().toObject());
1333
1334 const QJsonObject images = m_json.object().value(KEY_IMAGES).toObject();
1335 for (auto it = images.begin(), end = images.end(); it != end; ++it)
1336 processJSONImage(id: it.key(), jsonObject: it.value().toObject());
1337
1338 const QJsonObject textures = m_json.object().value(KEY_TEXTURES).toObject();
1339 for (auto it = textures.begin(), end = textures.end(); it != end; ++it)
1340 processJSONTexture(id: it.key(), jsonObject: it.value().toObject());
1341
1342 const QJsonObject extensions = m_json.object().value(KEY_EXTENSIONS).toObject();
1343 for (auto it = extensions.begin(), end = extensions.end(); it != end; ++it)
1344 processJSONExtensions(id: it.key(), jsonObject: it.value().toObject());
1345
1346 const QJsonObject passes = m_json.object().value(KEY_RENDERPASSES).toObject();
1347 for (auto it = passes.begin(), end = passes.end(); it != end; ++it)
1348 processJSONRenderPass(id: it.key(), jsonObject: it.value().toObject());
1349
1350 const QJsonObject techniques = m_json.object().value(KEY_TECHNIQUES).toObject();
1351 for (auto it = techniques.begin(), end = techniques.end(); it != end; ++it)
1352 processJSONTechnique(id: it.key(), jsonObject: it.value().toObject());
1353
1354 const QJsonObject effects = m_json.object().value(KEY_EFFECTS).toObject();
1355 for (auto it = effects.begin(), end = effects.end(); it != end; ++it)
1356 processJSONEffect(id: it.key(), jsonObject: it.value().toObject());
1357
1358 m_defaultScene = m_json.object().value(KEY_SCENE).toString();
1359}
1360
1361void GLTFImporter::parseV2()
1362{
1363 int i;
1364 const QJsonArray buffers = m_json.object().value(KEY_BUFFERS).toArray();
1365 for (i = 0; i < buffers.count(); i++)
1366 processJSONBuffer(id: QString::number(i), json: buffers[i].toObject());
1367
1368 const QJsonArray views = m_json.object().value(KEY_BUFFER_VIEWS).toArray();
1369 loadBufferData();
1370 for (i = 0; i < views.count(); i++)
1371 processJSONBufferView(id: QString::number(i), json: views[i].toObject());
1372 unloadBufferData();
1373
1374 const QJsonArray accessors = m_json.object().value(KEY_ACCESSORS).toArray();
1375 for (i = 0; i < accessors.count(); i++)
1376 processJSONAccessor(id: QString::number(i), json: accessors[i].toObject());
1377
1378 const QJsonArray meshes = m_json.object().value(KEY_MESHES).toArray();
1379 for (i = 0; i < meshes.count(); i++)
1380 processJSONMesh(id: QString::number(i), json: meshes[i].toObject());
1381
1382 const QJsonArray images = m_json.object().value(KEY_IMAGES).toArray();
1383 for (i = 0; i < images.count(); i++)
1384 processJSONImage(id: QString::number(i), jsonObject: images[i].toObject());
1385
1386 const QJsonArray textures = m_json.object().value(KEY_TEXTURES).toArray();
1387 for (i = 0; i < textures.count(); i++)
1388 processJSONTexture(id: QString::number(i), jsonObject: textures[i].toObject());
1389
1390 m_defaultScene = QString::number(m_json.object().value(KEY_SCENE).toInt());
1391}
1392
1393namespace {
1394template <typename C>
1395void delete_if_without_parent(const C &c)
1396{
1397 for (const auto *e : c) {
1398 if (!e->parent())
1399 delete e;
1400 }
1401}
1402} // unnamed namespace
1403
1404void GLTFImporter::cleanup()
1405{
1406 m_meshDict.clear();
1407 m_meshMaterialDict.clear();
1408 m_accessorDict.clear();
1409 delete_if_without_parent(c: m_materialCache);
1410 m_materialCache.clear();
1411 m_bufferDatas.clear();
1412 m_buffers.clear();
1413 m_shaderPaths.clear();
1414 delete_if_without_parent(c: m_programs);
1415 m_programs.clear();
1416 for (const auto &params : std::as_const(t&: m_techniqueParameters))
1417 delete_if_without_parent(c: params);
1418 m_techniqueParameters.clear();
1419 delete_if_without_parent(c: m_techniques);
1420 m_techniques.clear();
1421 delete_if_without_parent(c: m_textures);
1422 m_textures.clear();
1423 m_imagePaths.clear();
1424 m_imageData.clear();
1425 m_defaultScene.clear();
1426 m_parameterDataDict.clear();
1427 delete_if_without_parent(c: m_renderPasses);
1428 m_renderPasses.clear();
1429 delete_if_without_parent(c: m_effects);
1430 m_effects.clear();
1431}
1432
1433void GLTFImporter::processJSONAsset(const QJsonObject &json)
1434{
1435 const QString version = json.value(KEY_VERSION).toString();
1436 if (!version.isEmpty()) {
1437 const QStringList verTokens = version.split(sep: '.');
1438 if (verTokens.size() >= 2) {
1439 m_majorVersion = verTokens[0].toInt();
1440 m_minorVersion = verTokens[1].toInt();
1441 }
1442 }
1443}
1444
1445void GLTFImporter::processJSONBuffer(const QString &id, const QJsonObject& json)
1446{
1447 // simply cache buffers for lookup by buffer-views
1448 m_bufferDatas[id] = BufferData(json);
1449}
1450
1451void GLTFImporter::processJSONBufferView(const QString &id, const QJsonObject& json)
1452{
1453 QString bufName;
1454 if (m_majorVersion > 1) {
1455 bufName = QString::number(json.value(KEY_BUFFER).toInt());
1456 } else {
1457 bufName = json.value(KEY_BUFFER).toString();
1458 }
1459 const auto it = std::as_const(t&: m_bufferDatas).find(key: bufName);
1460 if (Q_UNLIKELY(it == m_bufferDatas.cend())) {
1461 qCWarning(GLTFImporterLog, "unknown buffer: %ls processing view: %ls",
1462 qUtf16PrintableImpl(bufName), qUtf16PrintableImpl(id));
1463 return;
1464 }
1465 const auto &bufferData = *it;
1466
1467 quint64 offset = 0;
1468 const auto byteOffset = json.value(KEY_BYTE_OFFSET);
1469 if (!byteOffset.isUndefined()) {
1470 offset = byteOffset.toInt();
1471 qCDebug(GLTFImporterLog, "bv: %ls has offset: %lld", qUtf16PrintableImpl(id), offset);
1472 }
1473
1474 quint64 len = json.value(KEY_BYTE_LENGTH).toInt();
1475
1476 QByteArray bytes = bufferData.data->mid(index: offset, len);
1477 if (Q_UNLIKELY(bytes.size() != qsizetype(len))) {
1478 qCWarning(GLTFImporterLog, "failed to read sufficient bytes from: %ls for view %ls",
1479 qUtf16PrintableImpl(bufferData.path), qUtf16PrintableImpl(id));
1480 }
1481
1482 Qt3DCore::QBuffer *b = new Qt3DCore::QBuffer();
1483 b->setData(bytes);
1484 m_buffers[id] = b;
1485}
1486
1487void GLTFImporter::processJSONShader(const QString &id, const QJsonObject &jsonObject)
1488{
1489 // shaders are trivial for the moment, defer the real work
1490 // to the program section
1491 QString path = jsonObject.value(KEY_URI).toString();
1492
1493 if (!isEmbeddedResource(url: path)) {
1494 QFileInfo info(m_basePath, path);
1495 if (Q_UNLIKELY(!info.exists())) {
1496 qCWarning(GLTFImporterLog, "can't find shader %ls from path %ls",
1497 qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
1498 return;
1499 }
1500
1501 m_shaderPaths[id] = info.absoluteFilePath();
1502 } else {
1503 const QByteArray base64Data = path.toLatin1().remove(index: 0, len: path.indexOf(s: ",") + 1);
1504 m_shaderPaths[id] = QString(QByteArray::fromBase64(base64: base64Data));
1505 }
1506
1507
1508}
1509
1510void GLTFImporter::processJSONProgram(const QString &id, const QJsonObject &jsonObject)
1511{
1512 const QString fragName = jsonObject.value(KEY_FRAGMENT_SHADER).toString();
1513 const QString vertName = jsonObject.value(KEY_VERTEX_SHADER).toString();
1514
1515 const auto fragIt = std::as_const(t&: m_shaderPaths).find(key: fragName);
1516 const auto vertIt = std::as_const(t&: m_shaderPaths).find(key: vertName);
1517
1518 if (Q_UNLIKELY(fragIt == m_shaderPaths.cend() || vertIt == m_shaderPaths.cend())) {
1519 qCWarning(GLTFImporterLog, "program: %ls missing shader: %ls %ls",
1520 qUtf16PrintableImpl(id), qUtf16PrintableImpl(fragName), qUtf16PrintableImpl(vertName));
1521 return;
1522 }
1523
1524 QShaderProgram* prog = new QShaderProgram;
1525 prog->setObjectName(id);
1526 prog->setFragmentShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl::fromLocalFile(localfile: fragIt.value())));
1527 prog->setVertexShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl::fromLocalFile(localfile: vertIt.value())));
1528
1529 const QString tessCtrlName = jsonObject.value(KEY_TESS_CTRL_SHADER).toString();
1530 if (!tessCtrlName.isEmpty()) {
1531 const auto it = std::as_const(t&: m_shaderPaths).find(key: tessCtrlName);
1532 prog->setTessellationControlShaderCode(
1533 QShaderProgram::loadSource(sourceUrl: QUrl::fromLocalFile(localfile: it.value())));
1534 }
1535
1536 const QString tessEvalName = jsonObject.value(KEY_TESS_EVAL_SHADER).toString();
1537 if (!tessEvalName.isEmpty()) {
1538 const auto it = std::as_const(t&: m_shaderPaths).find(key: tessEvalName);
1539 prog->setTessellationEvaluationShaderCode(
1540 QShaderProgram::loadSource(sourceUrl: QUrl::fromLocalFile(localfile: it.value())));
1541 }
1542
1543 const QString geomName = jsonObject.value(KEY_GEOMETRY_SHADER).toString();
1544 if (!geomName.isEmpty()) {
1545 const auto it = std::as_const(t&: m_shaderPaths).find(key: geomName);
1546 prog->setGeometryShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl::fromLocalFile(localfile: it.value())));
1547 }
1548
1549 const QString computeName = jsonObject.value(KEY_COMPUTE_SHADER).toString();
1550 if (!computeName.isEmpty()) {
1551 const auto it = std::as_const(t&: m_shaderPaths).find(key: computeName);
1552 prog->setComputeShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl::fromLocalFile(localfile: it.value())));
1553 }
1554
1555 m_programs[id] = prog;
1556}
1557
1558void GLTFImporter::processJSONTechnique(const QString &id, const QJsonObject &jsonObject )
1559{
1560 QTechnique *t = new QTechnique;
1561 t->setObjectName(id);
1562
1563 const QJsonObject gabifilter = jsonObject.value(KEY_GABIFILTER).toObject();
1564 if (gabifilter.isEmpty()) {
1565 // Regular GLTF technique
1566
1567 // Parameters
1568 QHash<QString, QParameter *> paramDict;
1569 const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
1570 for (auto it = params.begin(), end = params.end(); it != end; ++it) {
1571 const QString pname = it.key();
1572 const QJsonObject po = it.value().toObject();
1573 QParameter *p = buildParameter(key: pname, paramObj: po);
1574 m_parameterDataDict.insert(key: p, value: ParameterData(po));
1575 // We don't want to insert invalid parameters to techniques themselves, but we
1576 // need to maintain link between all parameters and techniques so we can resolve
1577 // parameter type later when creating materials.
1578 if (p->value().isValid())
1579 t->addParameter(p);
1580 paramDict[pname] = p;
1581 }
1582
1583 // Program
1584 QRenderPass *pass = new QRenderPass;
1585 addProgramToPass(pass, progName: jsonObject.value(KEY_PROGRAM).toString());
1586
1587 // Attributes
1588 const QJsonObject attrs = jsonObject.value(KEY_ATTRIBUTES).toObject();
1589 for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
1590 QString pname = it.value().toString();
1591 QParameter *parameter = paramDict.value(key: pname, defaultValue: nullptr);
1592 QString attributeName = pname;
1593 if (Q_UNLIKELY(!parameter)) {
1594 qCWarning(GLTFImporterLog, "attribute %ls defined in instanceProgram but not as parameter",
1595 qUtf16PrintableImpl(pname));
1596 continue;
1597 }
1598 //Check if the parameter has a standard attribute semantic
1599 const auto paramDataIt = m_parameterDataDict.find(key: parameter);
1600 QString standardAttributeName = standardAttributeNameFromSemantic(semantic: paramDataIt->semantic);
1601 if (!standardAttributeName.isNull()) {
1602 attributeName = standardAttributeName;
1603 t->removeParameter(p: parameter);
1604 m_parameterDataDict.erase(it: paramDataIt);
1605 paramDict.remove(key: pname);
1606 delete parameter;
1607 }
1608
1609 } // of program-instance attributes
1610
1611 // Uniforms
1612 const QJsonObject uniforms = jsonObject.value(KEY_UNIFORMS).toObject();
1613 for (auto it = uniforms.begin(), end = uniforms.end(); it != end; ++it) {
1614 const QString pname = it.value().toString();
1615 QParameter *parameter = paramDict.value(key: pname, defaultValue: nullptr);
1616 if (Q_UNLIKELY(!parameter)) {
1617 qCWarning(GLTFImporterLog, "uniform %ls defined in instanceProgram but not as parameter",
1618 qUtf16PrintableImpl(pname));
1619 continue;
1620 }
1621 //Check if the parameter has a standard uniform semantic
1622 const auto paramDataIt = m_parameterDataDict.find(key: parameter);
1623 if (hasStandardUniformNameFromSemantic(semantic: paramDataIt->semantic)) {
1624 t->removeParameter(p: parameter);
1625 m_parameterDataDict.erase(it: paramDataIt);
1626 paramDict.remove(key: pname);
1627 delete parameter;
1628 }
1629 } // of program-instance uniforms
1630
1631 m_techniqueParameters.insert(key: t, value: paramDict.values());
1632
1633 populateRenderStates(pass, states: jsonObject.value(KEY_STATES).toObject());
1634
1635 t->addRenderPass(pass);
1636 } else {
1637 // Qt3D exported custom technique
1638
1639 // Graphics API filter
1640 t->graphicsApiFilter()->setApi(QGraphicsApiFilter::Api(gabifilter.value(KEY_API).toInt()));
1641 t->graphicsApiFilter()->setMajorVersion(gabifilter.value(KEY_MAJORVERSION).toInt());
1642 t->graphicsApiFilter()->setMinorVersion(gabifilter.value(KEY_MINORVERSION).toInt());
1643 t->graphicsApiFilter()->setProfile(QGraphicsApiFilter::OpenGLProfile(
1644 gabifilter.value(KEY_PROFILE).toInt()));
1645 t->graphicsApiFilter()->setVendor(gabifilter.value(KEY_VENDOR).toString());
1646 QStringList extensionList;
1647 QJsonArray extArray = gabifilter.value(KEY_EXTENSIONS).toArray();
1648 for (const QJsonValue extValue : extArray)
1649 extensionList << extValue.toString();
1650 t->graphicsApiFilter()->setExtensions(extensionList);
1651
1652 // Filter keys (we will assume filter keys are always strings or integers)
1653 const QJsonObject fkObj = jsonObject.value(KEY_FILTERKEYS).toObject();
1654 for (auto it = fkObj.begin(), end = fkObj.end(); it != end; ++it)
1655 t->addFilterKey(filterKey: buildFilterKey(key: it.key(), val: it.value()));
1656
1657 t->setObjectName(jsonObject.value(KEY_NAME).toString());
1658
1659 // Parameters
1660 const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
1661 for (auto it = params.begin(), end = params.end(); it != end; ++it)
1662 t->addParameter(p: buildParameter(key: it.key(), paramObj: it.value().toObject()));
1663
1664 // Render passes
1665 const QJsonArray passArray = jsonObject.value(KEY_RENDERPASSES).toArray();
1666 for (const QJsonValue passValue : passArray) {
1667 const QString passName = passValue.toString();
1668 QRenderPass *pass = m_renderPasses.value(key: passName);
1669 if (pass) {
1670 t->addRenderPass(pass);
1671 } else {
1672 qCWarning(GLTFImporterLog, "Render pass %ls missing for technique %ls",
1673 qUtf16PrintableImpl(passName), qUtf16PrintableImpl(id));
1674 }
1675 }
1676 }
1677
1678 m_techniques[id] = t;
1679}
1680
1681void GLTFImporter::processJSONAccessor(const QString &id, const QJsonObject& json)
1682{
1683 m_accessorDict[id] = AccessorData(json, m_majorVersion, m_minorVersion);
1684}
1685
1686void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
1687{
1688 const QString meshName = json.value(KEY_NAME).toString();
1689 const QString meshType = json.value(KEY_TYPE).toString();
1690 if (meshType.isEmpty()) {
1691 // Custom mesh
1692 const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
1693 for (const QJsonValue primitiveValue : primitivesArray) {
1694 const QJsonObject primitiveObject = primitiveValue.toObject();
1695 const QJsonValue type = primitiveObject.value(KEY_MODE);
1696 const QJsonValue matValue = primitiveObject.value(KEY_MATERIAL);
1697 const QString material = (m_majorVersion > 1) ? QString::number(matValue.toInt()) : matValue.toString();
1698
1699 QGeometryRenderer *geometryRenderer = new QGeometryRenderer;
1700 QGeometryView *geometryView = new QGeometryView;
1701 QGeometry *meshGeometry = new QGeometry(geometryRenderer);
1702
1703 //Set Primitive Type
1704 if (type.isUndefined()) {
1705 geometryRenderer->setPrimitiveType(QGeometryRenderer::Triangles);
1706 } else {
1707 geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type.toInt()));
1708 }
1709
1710 //Save Material for mesh
1711 m_meshMaterialDict[geometryRenderer] = material;
1712
1713 const QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject();
1714 for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
1715 const QString k = (m_majorVersion > 1) ? QString::number(it.value().toInt()) : it.value().toString();
1716 const auto accessorIt = std::as_const(t&: m_accessorDict).find(key: k);
1717 if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) {
1718 qCWarning(GLTFImporterLog, "unknown attribute accessor: %ls on mesh %ls",
1719 qUtf16PrintableImpl(k), qUtf16PrintableImpl(id));
1720 continue;
1721 }
1722
1723 const QString attrName = it.key();
1724 QString attributeName = standardAttributeNameFromSemantic(semantic: attrName);
1725 if (attributeName.isEmpty())
1726 attributeName = attrName;
1727
1728 //Get buffer handle for accessor
1729 Qt3DCore::QBuffer *buffer = m_buffers.value(key: accessorIt->bufferViewName, defaultValue: nullptr);
1730 if (Q_UNLIKELY(!buffer)) {
1731 qCWarning(GLTFImporterLog, "unknown buffer-view: %ls processing accessor: %ls",
1732 qUtf16PrintableImpl(accessorIt->bufferViewName),
1733 qUtf16PrintableImpl(id));
1734 continue;
1735 }
1736
1737 QAttribute *attribute = new QAttribute(buffer,
1738 attributeName,
1739 accessorIt->type,
1740 accessorIt->dataSize,
1741 accessorIt->count,
1742 accessorIt->offset,
1743 accessorIt->stride);
1744 attribute->setAttributeType(QAttribute::VertexAttribute);
1745 meshGeometry->addAttribute(attribute);
1746 }
1747
1748 const auto indices = primitiveObject.value(KEY_INDICES);
1749 if (!indices.isUndefined()) {
1750 const QString accIndex = (m_majorVersion > 1) ? QString::number(indices.toInt()) : indices.toString();
1751 const auto accessorIt = std::as_const(t&: m_accessorDict).find(key: accIndex);
1752 if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) {
1753 qCWarning(GLTFImporterLog, "unknown index accessor: %ls on mesh %ls",
1754 qUtf16PrintableImpl(accIndex), qUtf16PrintableImpl(id));
1755 } else {
1756 //Get buffer handle for accessor
1757 Qt3DCore::QBuffer *buffer = m_buffers.value(key: accessorIt->bufferViewName, defaultValue: nullptr);
1758 if (Q_UNLIKELY(!buffer)) {
1759 qCWarning(GLTFImporterLog, "unknown buffer-view: %ls processing accessor: %ls",
1760 qUtf16PrintableImpl(accessorIt->bufferViewName),
1761 qUtf16PrintableImpl(id));
1762 continue;
1763 }
1764
1765 QAttribute *attribute = new QAttribute(buffer,
1766 accessorIt->type,
1767 accessorIt->dataSize,
1768 accessorIt->count,
1769 accessorIt->offset,
1770 accessorIt->stride);
1771 attribute->setAttributeType(QAttribute::IndexAttribute);
1772 meshGeometry->addAttribute(attribute);
1773 }
1774 } // of has indices
1775
1776 geometryView->setGeometry(meshGeometry);
1777 geometryRenderer->setView(geometryView);
1778 geometryRenderer->setObjectName(meshName);
1779
1780 m_meshDict.insert(key: id, value: geometryRenderer);
1781 } // of primitives iteration
1782 } else {
1783 QGeometryRenderer *mesh = nullptr;
1784 if (meshType == QStringLiteral("cone")) {
1785 mesh = new QConeMesh;
1786 } else if (meshType == QStringLiteral("cuboid")) {
1787 mesh = new QCuboidMesh;
1788 } else if (meshType == QStringLiteral("cylinder")) {
1789 mesh = new QCylinderMesh;
1790 } else if (meshType == QStringLiteral("plane")) {
1791 mesh = new QPlaneMesh;
1792 } else if (meshType == QStringLiteral("sphere")) {
1793 mesh = new QSphereMesh;
1794 } else if (meshType == QStringLiteral("torus")) {
1795 mesh = new QTorusMesh;
1796 } else {
1797 qCWarning(GLTFImporterLog,
1798 "Invalid mesh type: %ls for mesh: %ls",
1799 qUtf16PrintableImpl(meshType),
1800 qUtf16PrintableImpl(id));
1801 }
1802
1803 if (mesh) {
1804 // Read and set properties
1805 const QJsonObject propObj = json.value(KEY_PROPERTIES).toObject();
1806 QObject *target = mesh;
1807 if (mesh->view())
1808 target = mesh->view();
1809 for (auto it = propObj.begin(), end = propObj.end(); it != end; ++it) {
1810 const QByteArray propName = it.key().toLatin1();
1811 // Basic mesh types only have bool, int, float, and QSize type properties
1812 if (it.value().isBool()) {
1813 target->setProperty(name: propName.constData(), value: QVariant(it.value().toBool()));
1814 } else if (it.value().isArray()) {
1815 const QJsonArray valueArray = it.value().toArray();
1816 if (valueArray.size() == 2) {
1817 QSize size;
1818 size.setWidth(valueArray.at(i: 0).toInt());
1819 size.setHeight(valueArray.at(i: 1).toInt());
1820 target->setProperty(name: propName.constData(), value: QVariant(size));
1821 }
1822 } else {
1823 const QMetaType propType = target->property(name: propName.constData()).metaType();
1824 if (propType.id() == QMetaType::Int) {
1825 target->setProperty(name: propName.constData(), value: QVariant(it.value().toInt()));
1826 } else {
1827 target->setProperty(name: propName.constData(),
1828 value: QVariant(float(it.value().toDouble())));
1829 }
1830 }
1831 }
1832 mesh->setObjectName(meshName);
1833 mesh->view()->setObjectName(meshName);
1834 m_meshMaterialDict[mesh] = (m_majorVersion > 1) ?
1835 QString::number(json.value(KEY_MATERIAL).toInt()) :
1836 json.value(KEY_MATERIAL).toString();
1837 m_meshDict.insert(key: id, value: mesh);
1838 }
1839 }
1840}
1841
1842void GLTFImporter::processJSONImage(const QString &id, const QJsonObject &jsonObject)
1843{
1844 QString path = jsonObject.value(KEY_URI).toString();
1845
1846 if (!isEmbeddedResource(url: path)) {
1847 QFileInfo info(m_basePath, path);
1848 if (Q_UNLIKELY(!info.exists())) {
1849 qCWarning(GLTFImporterLog, "can't find image %ls from path %ls",
1850 qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
1851 return;
1852 }
1853
1854 m_imagePaths[id] = info.absoluteFilePath();
1855 } else {
1856 const QByteArray base64Data = path.toLatin1().remove(index: 0, len: path.indexOf(s: ",") + 1);
1857 QImage image;
1858 image.loadFromData(data: QByteArray::fromBase64(base64: base64Data));
1859 m_imageData[id] = image;
1860 }
1861}
1862
1863void GLTFImporter::processJSONTexture(const QString &id, const QJsonObject &jsonObject)
1864{
1865 QJsonValue jsonVal = jsonObject.value(KEY_TARGET);
1866 if (!jsonVal.isUndefined()) {
1867 int target = jsonVal.toInt(GL_TEXTURE_2D);
1868 //TODO: support other targets that GL_TEXTURE_2D (though the spec doesn't support anything else)
1869 if (Q_UNLIKELY(target != GL_TEXTURE_2D)) {
1870 qCWarning(GLTFImporterLog, "unsupported texture target: %d", target);
1871 return;
1872 }
1873 }
1874
1875 QTexture2D* tex = new QTexture2D;
1876
1877 // TODO: Choose suitable internal format - may vary on OpenGL context type
1878 //int pixelFormat = jsonObj.value(KEY_FORMAT).toInt(GL_RGBA);
1879 int internalFormat = GL_RGBA;
1880 jsonVal = jsonObject.value(KEY_INTERNAL_FORMAT);
1881 if (!jsonVal.isUndefined())
1882 internalFormat = jsonObject.value(KEY_INTERNAL_FORMAT).toInt(GL_RGBA);
1883
1884 tex->setFormat(static_cast<QAbstractTexture::TextureFormat>(internalFormat));
1885
1886 QJsonValue srcValue = jsonObject.value(KEY_SOURCE);
1887 QString source = (m_majorVersion > 1) ? QString::number(srcValue.toInt()) : srcValue.toString();
1888
1889 const auto imagIt = std::as_const(t&: m_imagePaths).find(key: source);
1890 if (Q_UNLIKELY(imagIt == m_imagePaths.cend())) {
1891 // if an image is not found in paths, it probably means
1892 // it was an embedded resource, referenced in m_imageData
1893 const auto embImgIt = std::as_const(t&: m_imageData).find(key: source);
1894 if (Q_UNLIKELY(embImgIt == m_imageData.cend())) {
1895 qCWarning(GLTFImporterLog, "texture %ls references missing image %ls",
1896 qUtf16PrintableImpl(id), qUtf16PrintableImpl(source));
1897 return;
1898 }
1899
1900 QImage img = embImgIt.value();
1901 GLTFRawTextureImage *imageData = new GLTFRawTextureImage();
1902 imageData->setImage(img);
1903 tex->addTextureImage(textureImage: imageData);
1904 } else {
1905 QTextureImage *texImage = new QTextureImage(tex);
1906 texImage->setMirrored(false);
1907 texImage->setSource(QUrl::fromLocalFile(localfile: imagIt.value()));
1908 tex->addTextureImage(textureImage: texImage);
1909 }
1910
1911 setTextureSamplerInfo(id, jsonObj: jsonObject, tex);
1912
1913 m_textures[id] = tex;
1914}
1915
1916void GLTFImporter::processJSONExtensions(const QString &id, const QJsonObject &jsonObject)
1917{
1918 // Lights are defined in "KHR_materials_common" property of "extensions" property of the top
1919 // level GLTF item.
1920 if (id == KEY_COMMON_MAT) {
1921 const auto lights = jsonObject.value(KEY_LIGHTS).toObject();
1922 const auto keys = lights.keys();
1923 for (const auto &lightKey : keys) {
1924 const auto light = lights.value(key: lightKey).toObject();
1925 auto lightType = light.value(KEY_TYPE).toString();
1926 const auto lightValues = light.value(key: lightType).toObject();
1927 QAbstractLight *lightComp = nullptr;
1928 if (lightType == KEY_DIRECTIONAL_LIGHT) {
1929 auto dirLight = new QDirectionalLight;
1930 dirLight->setWorldDirection(
1931 jsonArrToVec3(array: lightValues.value(KEY_DIRECTION).toArray()));
1932 lightComp = dirLight;
1933 } else if (lightType == KEY_SPOT_LIGHT) {
1934 auto spotLight = new QSpotLight;
1935 spotLight->setLocalDirection(
1936 jsonArrToVec3(array: lightValues.value(KEY_DIRECTION).toArray()));
1937 spotLight->setConstantAttenuation(
1938 lightValues.value(KEY_CONST_ATTENUATION).toDouble());
1939 spotLight->setLinearAttenuation(
1940 lightValues.value(KEY_LINEAR_ATTENUATION).toDouble());
1941 spotLight->setQuadraticAttenuation(
1942 lightValues.value(KEY_QUAD_ATTENUATION).toDouble());
1943 spotLight->setCutOffAngle(
1944 lightValues.value(KEY_FALLOFF_ANGLE).toDouble());
1945 lightComp = spotLight;
1946 } else if (lightType == KEY_POINT_LIGHT) {
1947 auto pointLight = new QPointLight;
1948 pointLight->setConstantAttenuation(
1949 lightValues.value(KEY_CONST_ATTENUATION).toDouble());
1950 pointLight->setLinearAttenuation(
1951 lightValues.value(KEY_LINEAR_ATTENUATION).toDouble());
1952 pointLight->setQuadraticAttenuation(
1953 lightValues.value(KEY_QUAD_ATTENUATION).toDouble());
1954 lightComp = pointLight;
1955 } else if (lightType == KEY_AMBIENT_LIGHT) {
1956 qCWarning(GLTFImporterLog, "Ambient lights are not supported.");
1957 } else {
1958 qCWarning(GLTFImporterLog, "Unknown light type: %ls",
1959 qUtf16PrintableImpl(lightType));
1960 }
1961
1962 if (lightComp) {
1963 auto colorVal = lightValues.value(KEY_COLOR);
1964 lightComp->setColor(vec4ToQColor(vec4Var: parameterValueFromJSON(GL_FLOAT_VEC4, value: colorVal)));
1965 lightComp->setIntensity(lightValues.value(KEY_INTENSITY).toDouble());
1966 lightComp->setObjectName(light.value(KEY_NAME).toString());
1967
1968 m_lights.insert(key: lightKey, value: lightComp);
1969 }
1970 }
1971 }
1972
1973}
1974
1975void GLTFImporter::processJSONEffect(const QString &id, const QJsonObject &jsonObject)
1976{
1977 QEffect *effect = new QEffect;
1978 renameFromJson(json: jsonObject, object: effect);
1979
1980 const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
1981 for (auto it = params.begin(), end = params.end(); it != end; ++it)
1982 effect->addParameter(parameter: buildParameter(key: it.key(), paramObj: it.value().toObject()));
1983
1984 const QJsonArray techArray = jsonObject.value(KEY_TECHNIQUES).toArray();
1985 for (const QJsonValue techValue : techArray) {
1986 const QString techName = techValue.toString();
1987 QTechnique *tech = m_techniques.value(key: techName);
1988 if (tech) {
1989 effect->addTechnique(t: tech);
1990 } else {
1991 qCWarning(GLTFImporterLog, "Technique pass %ls missing for effect %ls",
1992 qUtf16PrintableImpl(techName), qUtf16PrintableImpl(id));
1993 }
1994 }
1995
1996 m_effects[id] = effect;
1997}
1998
1999void GLTFImporter::processJSONRenderPass(const QString &id, const QJsonObject &jsonObject)
2000{
2001 QRenderPass *pass = new QRenderPass;
2002 const QJsonObject passFkObj = jsonObject.value(KEY_FILTERKEYS).toObject();
2003 for (auto it = passFkObj.begin(), end = passFkObj.end(); it != end; ++it)
2004 pass->addFilterKey(filterKey: buildFilterKey(key: it.key(), val: it.value()));
2005
2006 const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
2007 for (auto it = params.begin(), end = params.end(); it != end; ++it)
2008 pass->addParameter(p: buildParameter(key: it.key(), paramObj: it.value().toObject()));
2009
2010 populateRenderStates(pass, states: jsonObject.value(KEY_STATES).toObject());
2011 addProgramToPass(pass, progName: jsonObject.value(KEY_PROGRAM).toString());
2012
2013 renameFromJson(json: jsonObject, object: pass);
2014
2015 m_renderPasses[id] = pass;
2016}
2017
2018/*!
2019 Loads raw data from the GLTF file into the buffer.
2020*/
2021void GLTFImporter::loadBufferData()
2022{
2023 for (auto &bufferData : m_bufferDatas) {
2024 if (!bufferData.data) {
2025 bufferData.data = new QByteArray(resolveLocalData(path: bufferData.path));
2026 }
2027 }
2028}
2029
2030/*!
2031 Removes all data from the buffer.
2032*/
2033void GLTFImporter::unloadBufferData()
2034{
2035 for (const auto &bufferData : std::as_const(t&: m_bufferDatas)) {
2036 QByteArray *data = bufferData.data;
2037 delete data;
2038 }
2039}
2040
2041QByteArray GLTFImporter::resolveLocalData(const QString &path) const
2042{
2043 QDir d(m_basePath);
2044 Q_ASSERT(d.exists());
2045
2046 // check for embedded data
2047 if (isEmbeddedResource(url: path)) {
2048 const QByteArray base64Data = path.toLatin1().remove(index: 0, len: path.indexOf(s: ",") + 1);
2049 return QByteArray::fromBase64(base64: base64Data);
2050 } else {
2051 const QString absPath = d.absoluteFilePath(fileName: path);
2052 QFile f(absPath);
2053 f.open(flags: QIODevice::ReadOnly);
2054 return f.readAll();
2055 }
2056}
2057
2058QVariant GLTFImporter::parameterValueFromJSON(int type, const QJsonValue &value) const
2059{
2060 if (value.isBool()) {
2061 if (type == GL_BOOL)
2062 return QVariant(static_cast<GLboolean>(value.toBool()));
2063 } else if (value.isString()) {
2064 if (type == GL_SAMPLER_2D) {
2065 //Textures are special because we need to do a lookup to return the
2066 //QAbstractTexture
2067 QString textureId = value.toString();
2068 const auto it = m_textures.find(key: textureId);
2069 if (Q_UNLIKELY(it == m_textures.end())) {
2070 qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
2071 return QVariant();
2072 } else {
2073 return QVariant::fromValue(value: it.value());
2074 }
2075 }
2076 } else if (value.isDouble()) {
2077 switch (type) {
2078 case GL_BYTE:
2079 return QVariant(static_cast<GLbyte>(value.toInt()));
2080 case GL_UNSIGNED_BYTE:
2081 return QVariant(static_cast<GLubyte>(value.toInt()));
2082 case GL_SHORT:
2083 return QVariant(static_cast<GLshort>(value.toInt()));
2084 case GL_UNSIGNED_SHORT:
2085 return QVariant(static_cast<GLushort>(value.toInt()));
2086 case GL_INT:
2087 return QVariant(static_cast<GLint>(value.toInt()));
2088 case GL_UNSIGNED_INT:
2089 return QVariant(static_cast<GLuint>(value.toInt()));
2090 case GL_FLOAT:
2091 return QVariant(static_cast<GLfloat>(value.toDouble()));
2092 default:
2093 break;
2094 }
2095 } else if (value.isArray()) {
2096
2097 const QJsonArray valueArray = value.toArray();
2098
2099 QVector2D vector2D;
2100 QVector3D vector3D;
2101 QVector4D vector4D;
2102 std::vector<float> dataMat2(4, 0.0f);
2103 std::vector<float> dataMat3(9, 0.0f);
2104
2105 switch (type) {
2106 case GL_BYTE:
2107 return QVariant(static_cast<GLbyte>(valueArray.first().toInt()));
2108 case GL_UNSIGNED_BYTE:
2109 return QVariant(static_cast<GLubyte>(valueArray.first().toInt()));
2110 case GL_SHORT:
2111 return QVariant(static_cast<GLshort>(valueArray.first().toInt()));
2112 case GL_UNSIGNED_SHORT:
2113 return QVariant(static_cast<GLushort>(valueArray.first().toInt()));
2114 case GL_INT:
2115 return QVariant(static_cast<GLint>(valueArray.first().toInt()));
2116 case GL_UNSIGNED_INT:
2117 return QVariant(static_cast<GLuint>(valueArray.first().toInt()));
2118 case GL_FLOAT:
2119 return QVariant(static_cast<GLfloat>(valueArray.first().toDouble()));
2120 case GL_FLOAT_VEC2:
2121 vector2D.setX(static_cast<GLfloat>(valueArray.at(i: 0).toDouble()));
2122 vector2D.setY(static_cast<GLfloat>(valueArray.at(i: 1).toDouble()));
2123 return QVariant(vector2D);
2124 case GL_FLOAT_VEC3:
2125 vector3D.setX(static_cast<GLfloat>(valueArray.at(i: 0).toDouble()));
2126 vector3D.setY(static_cast<GLfloat>(valueArray.at(i: 1).toDouble()));
2127 vector3D.setZ(static_cast<GLfloat>(valueArray.at(i: 2).toDouble()));
2128 return QVariant(vector3D);
2129 case GL_FLOAT_VEC4:
2130 vector4D.setX(static_cast<GLfloat>(valueArray.at(i: 0).toDouble()));
2131 vector4D.setY(static_cast<GLfloat>(valueArray.at(i: 1).toDouble()));
2132 vector4D.setZ(static_cast<GLfloat>(valueArray.at(i: 2).toDouble()));
2133 vector4D.setW(static_cast<GLfloat>(valueArray.at(i: 3).toDouble()));
2134 return QVariant(vector4D);
2135 case GL_INT_VEC2:
2136 vector2D.setX(static_cast<GLint>(valueArray.at(i: 0).toInt()));
2137 vector2D.setY(static_cast<GLint>(valueArray.at(i: 1).toInt()));
2138 return QVariant(vector2D);
2139 case GL_INT_VEC3:
2140 vector3D.setX(static_cast<GLint>(valueArray.at(i: 0).toInt()));
2141 vector3D.setY(static_cast<GLint>(valueArray.at(i: 1).toInt()));
2142 vector3D.setZ(static_cast<GLint>(valueArray.at(i: 2).toInt()));
2143 return QVariant(vector3D);
2144 case GL_INT_VEC4:
2145 vector4D.setX(static_cast<GLint>(valueArray.at(i: 0).toInt()));
2146 vector4D.setY(static_cast<GLint>(valueArray.at(i: 1).toInt()));
2147 vector4D.setZ(static_cast<GLint>(valueArray.at(i: 2).toInt()));
2148 vector4D.setW(static_cast<GLint>(valueArray.at(i: 3).toInt()));
2149 return QVariant(vector4D);
2150 case GL_BOOL:
2151 return QVariant(static_cast<GLboolean>(valueArray.first().toBool()));
2152 case GL_BOOL_VEC2:
2153 vector2D.setX(static_cast<GLboolean>(valueArray.at(i: 0).toBool()));
2154 vector2D.setY(static_cast<GLboolean>(valueArray.at(i: 1).toBool()));
2155 return QVariant(vector2D);
2156 case GL_BOOL_VEC3:
2157 vector3D.setX(static_cast<GLboolean>(valueArray.at(i: 0).toBool()));
2158 vector3D.setY(static_cast<GLboolean>(valueArray.at(i: 1).toBool()));
2159 vector3D.setZ(static_cast<GLboolean>(valueArray.at(i: 2).toBool()));
2160 return QVariant(vector3D);
2161 case GL_BOOL_VEC4:
2162 vector4D.setX(static_cast<GLboolean>(valueArray.at(i: 0).toBool()));
2163 vector4D.setY(static_cast<GLboolean>(valueArray.at(i: 1).toBool()));
2164 vector4D.setZ(static_cast<GLboolean>(valueArray.at(i: 2).toBool()));
2165 vector4D.setW(static_cast<GLboolean>(valueArray.at(i: 3).toBool()));
2166 return QVariant(vector4D);
2167 case GL_FLOAT_MAT2:
2168 //Matrix2x2 is in Row Major ordering (so we need to convert)
2169 dataMat2[0] = static_cast<GLfloat>(valueArray.at(i: 0).toDouble());
2170 dataMat2[1] = static_cast<GLfloat>(valueArray.at(i: 2).toDouble());
2171 dataMat2[2] = static_cast<GLfloat>(valueArray.at(i: 1).toDouble());
2172 dataMat2[3] = static_cast<GLfloat>(valueArray.at(i: 3).toDouble());
2173 return QVariant::fromValue(value: QMatrix2x2(dataMat2.data()));
2174 case GL_FLOAT_MAT3:
2175 //Matrix3x3 is in Row Major ordering (so we need to convert)
2176 dataMat3[0] = static_cast<GLfloat>(valueArray.at(i: 0).toDouble());
2177 dataMat3[1] = static_cast<GLfloat>(valueArray.at(i: 3).toDouble());
2178 dataMat3[2] = static_cast<GLfloat>(valueArray.at(i: 6).toDouble());
2179 dataMat3[3] = static_cast<GLfloat>(valueArray.at(i: 1).toDouble());
2180 dataMat3[4] = static_cast<GLfloat>(valueArray.at(i: 4).toDouble());
2181 dataMat3[5] = static_cast<GLfloat>(valueArray.at(i: 7).toDouble());
2182 dataMat3[6] = static_cast<GLfloat>(valueArray.at(i: 2).toDouble());
2183 dataMat3[7] = static_cast<GLfloat>(valueArray.at(i: 5).toDouble());
2184 dataMat3[8] = static_cast<GLfloat>(valueArray.at(i: 8).toDouble());
2185 return QVariant::fromValue(value: QMatrix3x3(dataMat3.data()));
2186 case GL_FLOAT_MAT4:
2187 //Matrix4x4 is Column Major ordering
2188 return QVariant(QMatrix4x4(static_cast<GLfloat>(valueArray.at(i: 0).toDouble()),
2189 static_cast<GLfloat>(valueArray.at(i: 1).toDouble()),
2190 static_cast<GLfloat>(valueArray.at(i: 2).toDouble()),
2191 static_cast<GLfloat>(valueArray.at(i: 3).toDouble()),
2192 static_cast<GLfloat>(valueArray.at(i: 4).toDouble()),
2193 static_cast<GLfloat>(valueArray.at(i: 5).toDouble()),
2194 static_cast<GLfloat>(valueArray.at(i: 6).toDouble()),
2195 static_cast<GLfloat>(valueArray.at(i: 7).toDouble()),
2196 static_cast<GLfloat>(valueArray.at(i: 8).toDouble()),
2197 static_cast<GLfloat>(valueArray.at(i: 9).toDouble()),
2198 static_cast<GLfloat>(valueArray.at(i: 10).toDouble()),
2199 static_cast<GLfloat>(valueArray.at(i: 11).toDouble()),
2200 static_cast<GLfloat>(valueArray.at(i: 12).toDouble()),
2201 static_cast<GLfloat>(valueArray.at(i: 13).toDouble()),
2202 static_cast<GLfloat>(valueArray.at(i: 14).toDouble()),
2203 static_cast<GLfloat>(valueArray.at(i: 15).toDouble())));
2204 case GL_SAMPLER_2D:
2205 return QVariant(valueArray.at(i: 0).toString());
2206 }
2207 }
2208 return QVariant();
2209}
2210
2211QAttribute::VertexBaseType GLTFImporter::accessorTypeFromJSON(int componentType)
2212{
2213 if (componentType == GL_BYTE) {
2214 return QAttribute::Byte;
2215 } else if (componentType == GL_UNSIGNED_BYTE) {
2216 return QAttribute::UnsignedByte;
2217 } else if (componentType == GL_SHORT) {
2218 return QAttribute::Short;
2219 } else if (componentType == GL_UNSIGNED_SHORT) {
2220 return QAttribute::UnsignedShort;
2221 } else if (componentType == GL_UNSIGNED_INT) {
2222 return QAttribute::UnsignedInt;
2223 } else if (componentType == GL_FLOAT) {
2224 return QAttribute::Float;
2225 }
2226
2227 //There shouldn't be an invalid case here
2228 qCWarning(GLTFImporterLog, "unsupported accessor type %d", componentType);
2229 return QAttribute::Float;
2230}
2231
2232uint GLTFImporter::accessorDataSizeFromJson(const QString &type)
2233{
2234 QString typeName = type.toUpper();
2235 if (typeName == QLatin1String("SCALAR"))
2236 return 1;
2237 if (typeName == QLatin1String("VEC2"))
2238 return 2;
2239 if (typeName == QLatin1String("VEC3"))
2240 return 3;
2241 if (typeName == QLatin1String("VEC4"))
2242 return 4;
2243 if (typeName == QLatin1String("MAT2"))
2244 return 4;
2245 if (typeName == QLatin1String("MAT3"))
2246 return 9;
2247 if (typeName == QLatin1String("MAT4"))
2248 return 16;
2249
2250 return 0;
2251}
2252
2253QRenderState *GLTFImporter::buildStateEnable(int state)
2254{
2255 int type = 0;
2256 //By calling buildState with QJsonValue(), a Render State with
2257 //default values is created.
2258
2259 switch (state) {
2260 case GL_BLEND:
2261 //It doesn't make sense to handle this state alone
2262 return nullptr;
2263 case GL_CULL_FACE:
2264 return buildState(QStringLiteral("cullFace"), value: QJsonValue(), type);
2265 case GL_DEPTH_TEST:
2266 return buildState(QStringLiteral("depthFunc"), value: QJsonValue(), type);
2267 case GL_POLYGON_OFFSET_FILL:
2268 return buildState(QStringLiteral("polygonOffset"), value: QJsonValue(), type);
2269 case GL_SAMPLE_ALPHA_TO_COVERAGE:
2270 return new QAlphaCoverage();
2271 case GL_SCISSOR_TEST:
2272 return buildState(QStringLiteral("scissor"), value: QJsonValue(), type);
2273 case GL_DITHER: // Qt3D Custom
2274 return new QDithering();
2275 case 0x809D: // GL_MULTISAMPLE - Qt3D Custom
2276 return new QMultiSampleAntiAliasing();
2277 case 0x884F: // GL_TEXTURE_CUBE_MAP_SEAMLESS - Qt3D Custom
2278 return new QSeamlessCubemap();
2279 default:
2280 break;
2281 }
2282
2283 qCWarning(GLTFImporterLog, "unsupported render state: %d", state);
2284
2285 return nullptr;
2286}
2287
2288QRenderState* GLTFImporter::buildState(const QString& functionName, const QJsonValue &value, int &type)
2289{
2290 type = -1;
2291 QJsonArray values = value.toArray();
2292
2293 if (functionName == QLatin1String("blendColor")) {
2294 type = GL_BLEND;
2295 //TODO: support render state blendColor
2296 qCWarning(GLTFImporterLog, "unsupported render state: %ls", qUtf16PrintableImpl(functionName));
2297 return nullptr;
2298 }
2299
2300 if (functionName == QLatin1String("blendEquationSeparate")) {
2301 type = GL_BLEND;
2302 //TODO: support settings blendEquation alpha
2303 QBlendEquation *blendEquation = new QBlendEquation;
2304 blendEquation->setBlendFunction((QBlendEquation::BlendFunction)values.at(i: 0).toInt(GL_FUNC_ADD));
2305 return blendEquation;
2306 }
2307
2308 if (functionName == QLatin1String("blendFuncSeparate")) {
2309 type = GL_BLEND;
2310 QBlendEquationArguments *blendArgs = new QBlendEquationArguments;
2311 blendArgs->setSourceRgb((QBlendEquationArguments::Blending)values.at(i: 0).toInt(GL_ONE));
2312 blendArgs->setSourceAlpha((QBlendEquationArguments::Blending)values.at(i: 1).toInt(GL_ONE));
2313 blendArgs->setDestinationRgb((QBlendEquationArguments::Blending)values.at(i: 2).toInt(GL_ZERO));
2314 blendArgs->setDestinationAlpha((QBlendEquationArguments::Blending)values.at(i: 3).toInt(GL_ZERO));
2315 blendArgs->setBufferIndex(values.at(i: 4).toInt(defaultValue: -1));
2316 return blendArgs;
2317 }
2318
2319 if (functionName == QLatin1String("colorMask")) {
2320 QColorMask *colorMask = new QColorMask;
2321 colorMask->setRedMasked(values.at(i: 0).toBool(defaultValue: true));
2322 colorMask->setGreenMasked(values.at(i: 1).toBool(defaultValue: true));
2323 colorMask->setBlueMasked(values.at(i: 2).toBool(defaultValue: true));
2324 colorMask->setAlphaMasked(values.at(i: 3).toBool(defaultValue: true));
2325 return colorMask;
2326 }
2327
2328 if (functionName == QLatin1String("cullFace")) {
2329 type = GL_CULL_FACE;
2330 QCullFace *cullFace = new QCullFace;
2331 cullFace->setMode((QCullFace::CullingMode)values.at(i: 0).toInt(GL_BACK));
2332 return cullFace;
2333 }
2334
2335 if (functionName == QLatin1String("depthFunc")) {
2336 type = GL_DEPTH_TEST;
2337 QDepthTest *depthTest = new QDepthTest;
2338 depthTest->setDepthFunction((QDepthTest::DepthFunction)values.at(i: 0).toInt(GL_LESS));
2339 return depthTest;
2340 }
2341
2342 if (functionName == QLatin1String("depthMask")) {
2343 if (!values.at(i: 0).toBool(defaultValue: true)) {
2344 QNoDepthMask *depthMask = new QNoDepthMask;
2345 return depthMask;
2346 }
2347 return nullptr;
2348 }
2349
2350 if (functionName == QLatin1String("depthRange")) {
2351 type = GL_DEPTH_RANGE;
2352 QDepthRange *depthRange = new QDepthRange;
2353 depthRange->setNearValue(values.at(i: 0).toDouble(defaultValue: 0.0));
2354 depthRange->setFarValue(values.at(i: 1).toDouble(defaultValue: 1.0));
2355 return depthRange;
2356 }
2357
2358 if (functionName == QLatin1String("frontFace")) {
2359 QFrontFace *frontFace = new QFrontFace;
2360 frontFace->setDirection((QFrontFace::WindingDirection)values.at(i: 0).toInt(GL_CCW));
2361 return frontFace;
2362 }
2363
2364 if (functionName == QLatin1String("lineWidth")) {
2365 //TODO: support render state lineWidth
2366 qCWarning(GLTFImporterLog, "unsupported render state: %ls", qUtf16PrintableImpl(functionName));
2367 return nullptr;
2368 }
2369
2370 if (functionName == QLatin1String("polygonOffset")) {
2371 type = GL_POLYGON_OFFSET_FILL;
2372 QPolygonOffset *polygonOffset = new QPolygonOffset;
2373 polygonOffset->setScaleFactor((float)values.at(i: 0).toDouble(defaultValue: 0.0f));
2374 polygonOffset->setDepthSteps((float)values.at(i: 1).toDouble(defaultValue: 0.0f));
2375 return polygonOffset;
2376 }
2377
2378 if (functionName == QLatin1String("scissor")) {
2379 type = GL_SCISSOR_TEST;
2380 QScissorTest *scissorTest = new QScissorTest;
2381 scissorTest->setLeft(values.at(i: 0).toDouble(defaultValue: 0.0f));
2382 scissorTest->setBottom(values.at(i: 1).toDouble(defaultValue: 0.0f));
2383 scissorTest->setWidth(values.at(i: 2).toDouble(defaultValue: 0.0f));
2384 scissorTest->setHeight(values.at(i: 3).toDouble(defaultValue: 0.0f));
2385 return scissorTest;
2386 }
2387
2388 // Qt3D custom functions
2389 if (functionName == QLatin1String("alphaTest")) {
2390 QAlphaTest *args = new QAlphaTest;
2391 args->setAlphaFunction(QAlphaTest::AlphaFunction(values.at(i: 0).toInt()));
2392 args->setReferenceValue(float(values.at(i: 1).toDouble()));
2393 return args;
2394 }
2395
2396 if (functionName == QLatin1String("clipPlane")) {
2397 QClipPlane *args = new QClipPlane;
2398 args->setPlaneIndex(values.at(i: 0).toInt());
2399 args->setNormal(QVector3D(float(values.at(i: 1).toDouble()),
2400 float(values.at(i: 2).toDouble()),
2401 float(values.at(i: 3).toDouble())));
2402 args->setDistance(float(values.at(i: 4).toDouble()));
2403 return args;
2404 }
2405
2406 if (functionName == QLatin1String("pointSize")) {
2407 QPointSize *pointSize = new QPointSize;
2408 pointSize->setSizeMode(QPointSize::SizeMode(values.at(i: 0).toInt(defaultValue: QPointSize::Programmable)));
2409 pointSize->setValue(float(values.at(i: 1).toDouble()));
2410 return pointSize;
2411 }
2412
2413 if (functionName == QLatin1String("stencilMask")) {
2414 QStencilMask *stencilMask = new QStencilMask;
2415 stencilMask->setFrontOutputMask(uint(values.at(i: 0).toInt()));
2416 stencilMask->setBackOutputMask(uint(values.at(i: 1).toInt()));
2417 return stencilMask;
2418 }
2419
2420 if (functionName == QLatin1String("stencilOperation")) {
2421 QStencilOperation *stencilOperation = new QStencilOperation;
2422 stencilOperation->front()->setStencilTestFailureOperation(
2423 QStencilOperationArguments::Operation(values.at(i: 0).toInt(
2424 defaultValue: QStencilOperationArguments::Keep)));
2425 stencilOperation->front()->setDepthTestFailureOperation(
2426 QStencilOperationArguments::Operation(values.at(i: 1).toInt(
2427 defaultValue: QStencilOperationArguments::Keep)));
2428 stencilOperation->front()->setAllTestsPassOperation(
2429 QStencilOperationArguments::Operation(values.at(i: 2).toInt(
2430 defaultValue: QStencilOperationArguments::Keep)));
2431 stencilOperation->back()->setStencilTestFailureOperation(
2432 QStencilOperationArguments::Operation(values.at(i: 3).toInt(
2433 defaultValue: QStencilOperationArguments::Keep)));
2434 stencilOperation->back()->setDepthTestFailureOperation(
2435 QStencilOperationArguments::Operation(values.at(i: 4).toInt(
2436 defaultValue: QStencilOperationArguments::Keep)));
2437 stencilOperation->back()->setAllTestsPassOperation(
2438 QStencilOperationArguments::Operation(values.at(i: 5).toInt(
2439 defaultValue: QStencilOperationArguments::Keep)));
2440 return stencilOperation;
2441 }
2442
2443 if (functionName == QLatin1String("stencilTest")) {
2444 QStencilTest *stencilTest = new QStencilTest;
2445 stencilTest->front()->setComparisonMask(uint(values.at(i: 0).toInt()));
2446 stencilTest->front()->setReferenceValue(values.at(i: 1).toInt());
2447 stencilTest->front()->setStencilFunction(
2448 QStencilTestArguments::StencilFunction(values.at(i: 2).toInt(
2449 defaultValue: QStencilTestArguments::Never)));
2450 stencilTest->back()->setComparisonMask(uint(values.at(i: 3).toInt()));
2451 stencilTest->back()->setReferenceValue(values.at(i: 4).toInt());
2452 stencilTest->back()->setStencilFunction(
2453 QStencilTestArguments::StencilFunction(values.at(i: 5).toInt(
2454 defaultValue: QStencilTestArguments::Never)));
2455 return stencilTest;
2456 }
2457
2458 qCWarning(GLTFImporterLog, "unsupported render state: %ls", qUtf16PrintableImpl(functionName));
2459 return nullptr;
2460}
2461
2462QParameter *GLTFImporter::buildParameter(const QString &key, const QJsonObject &paramObj)
2463{
2464 QParameter *p = new QParameter;
2465 p->setName(key);
2466 QJsonValue value = paramObj.value(KEY_VALUE);
2467
2468 if (!value.isUndefined()) {
2469 int dataType = paramObj.value(KEY_TYPE).toInt();
2470 p->setValue(parameterValueFromJSON(type: dataType, value));
2471 }
2472
2473 return p;
2474}
2475
2476void GLTFImporter::populateRenderStates(QRenderPass *pass, const QJsonObject &states)
2477{
2478 // Process states to enable
2479 const QJsonArray enableStatesArray = states.value(KEY_ENABLE).toArray();
2480 QList<int> enableStates;
2481 for (const QJsonValue enableValue : enableStatesArray)
2482 enableStates.append(t: enableValue.toInt());
2483
2484 // Process the list of state functions
2485 const QJsonObject functions = states.value(KEY_FUNCTIONS).toObject();
2486 for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
2487 int enableStateType = 0;
2488 QRenderState *renderState = buildState(functionName: it.key(), value: it.value(), type&: enableStateType);
2489 if (renderState != nullptr) {
2490 //Remove the need to set a default state values for enableStateType
2491 enableStates.removeOne(t: enableStateType);
2492 pass->addRenderState(state: renderState);
2493 }
2494 }
2495
2496 // Create render states with default values for any remaining enable states
2497 for (int enableState : std::as_const(t&: enableStates)) {
2498 QRenderState *renderState = buildStateEnable(state: enableState);
2499 if (renderState != nullptr)
2500 pass->addRenderState(state: renderState);
2501 }
2502}
2503
2504void GLTFImporter::addProgramToPass(QRenderPass *pass, const QString &progName)
2505{
2506 const auto progIt = std::as_const(t&: m_programs).find(key: progName);
2507 if (Q_UNLIKELY(progIt == m_programs.cend()))
2508 qCWarning(GLTFImporterLog, "missing program %ls", qUtf16PrintableImpl(progName));
2509 else
2510 pass->setShaderProgram(progIt.value());
2511}
2512
2513void GLTFImporter::setTextureSamplerInfo(const QString &id, const QJsonObject &jsonObj, QTexture2D *tex)
2514{
2515 QJsonObject sampler;
2516 const QJsonValue jsonValue = jsonObj.value(KEY_SAMPLER);
2517 if (jsonValue.isUndefined())
2518 return;
2519
2520 if (m_majorVersion > 1) {
2521 const int samplerId = jsonValue.toInt();
2522 const QJsonArray sArray = m_json.object().value(KEY_SAMPLERS).toArray();
2523 if (Q_UNLIKELY(samplerId >= sArray.count())) {
2524 qCWarning(GLTFImporterLog, "texture %ls references unknown sampler %d",
2525 qUtf16PrintableImpl(id), samplerId);
2526 return;
2527 }
2528 sampler = sArray[samplerId].toObject();
2529 } else {
2530 const QString samplerId = jsonValue.toString();
2531 const QJsonValue samplersDictValue = m_json.object().value(KEY_SAMPLERS).toObject().value(key: samplerId);
2532 if (Q_UNLIKELY(samplersDictValue.isUndefined())) {
2533 qCWarning(GLTFImporterLog, "texture %ls references unknown sampler %ls",
2534 qUtf16PrintableImpl(id), qUtf16PrintableImpl(samplerId));
2535 return;
2536 }
2537 sampler = samplersDictValue.toObject();
2538 }
2539
2540 tex->setWrapMode(QTextureWrapMode(static_cast<QTextureWrapMode::WrapMode>(sampler.value(KEY_WRAP_S).toInt())));
2541 tex->setMinificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MIN_FILTER).toInt()));
2542 if (tex->minificationFilter() == QAbstractTexture::NearestMipMapLinear ||
2543 tex->minificationFilter() == QAbstractTexture::LinearMipMapNearest ||
2544 tex->minificationFilter() == QAbstractTexture::NearestMipMapNearest ||
2545 tex->minificationFilter() == QAbstractTexture::LinearMipMapLinear) {
2546
2547 tex->setGenerateMipMaps(true);
2548 }
2549 tex->setMagnificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MAG_FILTER).toInt()));
2550}
2551
2552GLTFRawTextureImage::GLTFRawTextureImage(QNode *parent)
2553 : QAbstractTextureImage(parent)
2554{
2555}
2556
2557QTextureImageDataGeneratorPtr GLTFRawTextureImage::dataGenerator() const
2558{
2559 return QTextureImageDataGeneratorPtr(new GLTFRawTextureImageFunctor(m_image));
2560}
2561
2562void GLTFRawTextureImage::setImage(const QImage &image)
2563{
2564 if (image != m_image) {
2565 m_image = image;
2566 notifyDataGeneratorChanged();
2567 }
2568}
2569
2570GLTFRawTextureImage::GLTFRawTextureImageFunctor::GLTFRawTextureImageFunctor(const QImage &image)
2571 : QTextureImageDataGenerator()
2572 , m_image(image)
2573{
2574}
2575
2576QTextureImageDataPtr GLTFRawTextureImage::GLTFRawTextureImageFunctor::operator()()
2577{
2578 QTextureImageDataPtr dataPtr = QTextureImageDataPtr::create();
2579 // Note: we assume 4 components per pixel and not compressed for now
2580 dataPtr->setImage(m_image);
2581 return dataPtr;
2582}
2583
2584bool GLTFRawTextureImage::GLTFRawTextureImageFunctor::operator ==(const QTextureImageDataGenerator &other) const
2585{
2586 const GLTFRawTextureImageFunctor *otherFunctor = functor_cast<GLTFRawTextureImageFunctor>(other: &other);
2587 return (otherFunctor != nullptr && otherFunctor->m_image == m_image);
2588}
2589
2590} // namespace Qt3DRender
2591
2592QT_END_NAMESPACE
2593
2594#include "moc_gltfimporter.cpp"
2595#include "gltfimporter.moc"
2596

source code of qt3d/src/plugins/sceneparsers/gltf/gltfimporter.cpp