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

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