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