1/****************************************************************************
2**
3** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "gltfgeometryloader.h"
41
42#include <QtCore/QDir>
43#include <QtCore/QJsonArray>
44#include <QtCore/QJsonObject>
45#include <QtCore/QVersionNumber>
46
47#include <QOpenGLTexture>
48
49#include <Qt3DRender/QGeometry>
50#include <Qt3DRender/private/renderlogging_p.h>
51#include <Qt3DCore/private/qloadgltf_p.h>
52
53QT_BEGIN_NAMESPACE
54
55#ifndef qUtf16PrintableImpl // -Impl is a Qt 5.8 feature
56# define qUtf16PrintableImpl(string) \
57 static_cast<const wchar_t*>(static_cast<const void*>(string.utf16()))
58#endif
59
60namespace Qt3DRender {
61
62Q_LOGGING_CATEGORY(GLTFGeometryLoaderLog, "Qt3D.GLTFGeometryLoader", QtWarningMsg)
63
64#define KEY_ASSET QLatin1String("asset")
65#define KEY_ACCESSORS QLatin1String("accessors")
66#define KEY_ATTRIBUTES QLatin1String("attributes")
67#define KEY_BUFFER QLatin1String("buffer")
68#define KEY_BUFFERS QLatin1String("buffers")
69#define KEY_BYTE_LENGTH QLatin1String("byteLength")
70#define KEY_BYTE_OFFSET QLatin1String("byteOffset")
71#define KEY_BYTE_STRIDE QLatin1String("byteStride")
72#define KEY_COUNT QLatin1String("count")
73#define KEY_INDICES QLatin1String("indices")
74#define KEY_MATERIAL QLatin1String("material")
75#define KEY_MESHES QLatin1String("meshes")
76#define KEY_NAME QLatin1String("name")
77#define KEY_PRIMITIVES QLatin1String("primitives")
78#define KEY_TARGET QLatin1String("target")
79#define KEY_TYPE QLatin1String("type")
80#define KEY_URI QLatin1String("uri")
81#define KEY_VERSION QLatin1String("version")
82
83#define KEY_BUFFER_VIEW QLatin1String("bufferView")
84#define KEY_BUFFER_VIEWS QLatin1String("bufferViews")
85#define KEY_COMPONENT_TYPE QLatin1String("componentType")
86
87GLTFGeometryLoader::GLTFGeometryLoader()
88 : m_geometry(nullptr)
89{
90}
91
92GLTFGeometryLoader::~GLTFGeometryLoader()
93{
94 cleanup();
95}
96
97QGeometry *GLTFGeometryLoader::geometry() const
98{
99 return m_geometry;
100}
101
102bool GLTFGeometryLoader::load(QIODevice *ioDev, const QString &subMesh)
103{
104 Q_UNUSED(subMesh);
105
106 if (Q_UNLIKELY(!setJSON(qLoadGLTF(ioDev->readAll())))) {
107 qCWarning(GLTFGeometryLoaderLog, "not a JSON document");
108 return false;
109 }
110
111 auto file = qobject_cast<QFile*>(object: ioDev);
112 if (file) {
113 QFileInfo finfo(file->fileName());
114 setBasePath(finfo.dir().absolutePath());
115 }
116
117 m_mesh = subMesh;
118
119 parse();
120
121 return true;
122}
123
124GLTFGeometryLoader::BufferData::BufferData()
125 : length(0)
126 , data(nullptr)
127{
128}
129
130GLTFGeometryLoader::BufferData::BufferData(const QJsonObject &json)
131 : length(json.value(KEY_BYTE_LENGTH).toInt())
132 , path(json.value(KEY_URI).toString())
133 , data(nullptr)
134{
135}
136
137GLTFGeometryLoader::AccessorData::AccessorData()
138 : bufferViewIndex(0)
139 , type(QAttribute::Float)
140 , dataSize(0)
141 , count(0)
142 , offset(0)
143 , stride(0)
144{
145
146}
147
148GLTFGeometryLoader::AccessorData::AccessorData(const QJsonObject &json)
149 : bufferViewName(json.value(KEY_BUFFER_VIEW).toString())
150 , bufferViewIndex(json.value(KEY_BUFFER_VIEW).toInt(defaultValue: -1))
151 , type(accessorTypeFromJSON(componentType: json.value(KEY_COMPONENT_TYPE).toInt()))
152 , dataSize(accessorDataSizeFromJson(type: json.value(KEY_TYPE).toString()))
153 , count(json.value(KEY_COUNT).toInt())
154 , offset(0)
155 , stride(0)
156{
157 const auto byteOffset = json.value(KEY_BYTE_OFFSET);
158 if (!byteOffset.isUndefined())
159 offset = byteOffset.toInt();
160 const auto byteStride = json.value(KEY_BYTE_STRIDE);
161 if (!byteStride.isUndefined())
162 stride = byteStride.toInt();
163}
164
165void GLTFGeometryLoader::setBasePath(const QString &path)
166{
167 m_basePath = path;
168}
169
170bool GLTFGeometryLoader::setJSON(const QJsonDocument &json )
171{
172 if (!json.isObject())
173 return false;
174
175 m_json = json;
176
177 cleanup();
178
179 return true;
180}
181
182QString GLTFGeometryLoader::standardAttributeNameFromSemantic(const QString &semantic)
183{
184 //Standard Attributes
185 if (semantic.startsWith(s: QLatin1String("POSITION")))
186 return QAttribute::defaultPositionAttributeName();
187 if (semantic.startsWith(s: QLatin1String("NORMAL")))
188 return QAttribute::defaultNormalAttributeName();
189 if (semantic.startsWith(s: QLatin1String("TEXCOORD")))
190 return QAttribute::defaultTextureCoordinateAttributeName();
191 if (semantic.startsWith(s: QLatin1String("COLOR")))
192 return QAttribute::defaultColorAttributeName();
193 if (semantic.startsWith(s: QLatin1String("TANGENT")))
194 return QAttribute::defaultTangentAttributeName();
195 if (semantic.startsWith(s: QLatin1String("JOINTS")))
196 return QAttribute::defaultJointIndicesAttributeName();
197 if (semantic.startsWith(s: QLatin1String("WEIGHTS")))
198 return QAttribute::defaultJointWeightsAttributeName();
199
200 return QString();
201}
202
203void GLTFGeometryLoader::parse()
204{
205 // Find the glTF version
206 const QJsonObject asset = m_json.object().value(KEY_ASSET).toObject();
207 const QString versionString = asset.value(KEY_VERSION).toString();
208 const auto version = QVersionNumber::fromString(string: versionString);
209 switch (version.majorVersion()) {
210 case 1:
211 parseGLTF1();
212 break;
213
214 case 2:
215 parseGLTF2();
216 break;
217
218 default:
219 qWarning() << "Unsupported version of glTF" << versionString;
220 }
221}
222
223void GLTFGeometryLoader::parseGLTF1()
224{
225 const QJsonObject buffers = m_json.object().value(KEY_BUFFERS).toObject();
226 for (auto it = buffers.begin(), end = buffers.end(); it != end; ++it)
227 processJSONBuffer(id: it.key(), json: it.value().toObject());
228
229 const QJsonObject views = m_json.object().value(KEY_BUFFER_VIEWS).toObject();
230 loadBufferData();
231 for (auto it = views.begin(), end = views.end(); it != end; ++it)
232 processJSONBufferView(id: it.key(), json: it.value().toObject());
233 unloadBufferData();
234
235 const QJsonObject attrs = m_json.object().value(KEY_ACCESSORS).toObject();
236 for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it)
237 processJSONAccessor(id: it.key(), json: it.value().toObject());
238
239 const QJsonObject meshes = m_json.object().value(KEY_MESHES).toObject();
240 for (auto it = meshes.begin(), end = meshes.end(); it != end && !m_geometry; ++it) {
241 const QJsonObject &mesh = it.value().toObject();
242 if (m_mesh.isEmpty() ||
243 m_mesh.compare(s: mesh.value(KEY_NAME).toString(), cs: Qt::CaseInsensitive) == 0)
244 processJSONMesh(id: it.key(), json: mesh);
245 }
246}
247
248void GLTFGeometryLoader::parseGLTF2()
249{
250 const QJsonArray buffers = m_json.object().value(KEY_BUFFERS).toArray();
251 for (auto it = buffers.begin(), end = buffers.end(); it != end; ++it)
252 processJSONBufferV2(json: it->toObject());
253
254 const QJsonArray views = m_json.object().value(KEY_BUFFER_VIEWS).toArray();
255 loadBufferDataV2();
256 for (auto it = views.begin(), end = views.end(); it != end; ++it)
257 processJSONBufferViewV2(json: it->toObject());
258 unloadBufferDataV2();
259
260 const QJsonArray attrs = m_json.object().value(KEY_ACCESSORS).toArray();
261 for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it)
262 processJSONAccessorV2(json: it->toObject());
263
264 const QJsonArray meshes = m_json.object().value(KEY_MESHES).toArray();
265 for (auto it = meshes.begin(), end = meshes.end(); it != end && !m_geometry; ++it) {
266 const QJsonObject &mesh = it->toObject();
267 if (m_mesh.isEmpty() || m_mesh.compare(s: mesh.value(KEY_NAME).toString(), cs: Qt::CaseInsensitive) == 0)
268 processJSONMeshV2(json: mesh);
269 }
270}
271
272void GLTFGeometryLoader::cleanup()
273{
274 m_geometry = nullptr;
275 m_gltf1.m_accessorDict.clear();
276 m_gltf1.m_buffers.clear();
277}
278
279void GLTFGeometryLoader::processJSONBuffer(const QString &id, const QJsonObject &json)
280{
281 // simply cache buffers for lookup by buffer-views
282 m_gltf1.m_bufferDatas[id] = BufferData(json);
283}
284
285void GLTFGeometryLoader::processJSONBufferV2(const QJsonObject &json)
286{
287 // simply cache buffers for lookup by buffer-views
288 m_gltf2.m_bufferDatas.push_back(t: BufferData(json));
289}
290
291void GLTFGeometryLoader::processJSONBufferView(const QString &id, const QJsonObject &json)
292{
293 QString bufName = json.value(KEY_BUFFER).toString();
294 const auto it = qAsConst(t&: m_gltf1.m_bufferDatas).find(akey: bufName);
295 if (Q_UNLIKELY(it == m_gltf1.m_bufferDatas.cend())) {
296 qCWarning(GLTFGeometryLoaderLog, "unknown buffer: %ls processing view: %ls",
297 qUtf16PrintableImpl(bufName), qUtf16PrintableImpl(id));
298 return;
299 }
300 const auto &bufferData = *it;
301
302 int target = json.value(KEY_TARGET).toInt();
303
304 switch (target) {
305 case GL_ARRAY_BUFFER:
306 case GL_ELEMENT_ARRAY_BUFFER:
307 break;
308 default:
309 qCWarning(GLTFGeometryLoaderLog, "buffer %ls unsupported target: %d",
310 qUtf16PrintableImpl(id), target);
311 return;
312 }
313
314 quint64 offset = 0;
315 const auto byteOffset = json.value(KEY_BYTE_OFFSET);
316 if (!byteOffset.isUndefined()) {
317 offset = byteOffset.toInt();
318 qCDebug(GLTFGeometryLoaderLog, "bv: %ls has offset: %lld", qUtf16PrintableImpl(id), offset);
319 }
320
321 const quint64 len = json.value(KEY_BYTE_LENGTH).toInt();
322
323 QByteArray bytes = bufferData.data->mid(index: offset, len);
324 if (Q_UNLIKELY(bytes.count() != int(len))) {
325 qCWarning(GLTFGeometryLoaderLog, "failed to read sufficient bytes from: %ls for view %ls",
326 qUtf16PrintableImpl(bufferData.path), qUtf16PrintableImpl(id));
327 }
328
329 Qt3DRender::QBuffer *b = new Qt3DRender::QBuffer();
330 b->setData(bytes);
331 m_gltf1.m_buffers[id] = b;
332}
333
334void GLTFGeometryLoader::processJSONBufferViewV2(const QJsonObject &json)
335{
336 const int bufferIndex = json.value(KEY_BUFFER).toInt();
337 if (Q_UNLIKELY(bufferIndex) >= m_gltf2.m_bufferDatas.size()) {
338 qCWarning(GLTFGeometryLoaderLog, "unknown buffer: %d processing view", bufferIndex);
339 return;
340 }
341 const auto bufferData = m_gltf2.m_bufferDatas[bufferIndex];
342
343 int target = json.value(KEY_TARGET).toInt();
344 switch (target) {
345 case GL_ARRAY_BUFFER:
346 case GL_ELEMENT_ARRAY_BUFFER:
347 break;
348 default:
349 return;
350 }
351
352 quint64 offset = 0;
353 const auto byteOffset = json.value(KEY_BYTE_OFFSET);
354 if (!byteOffset.isUndefined()) {
355 offset = byteOffset.toInt();
356 qCDebug(GLTFGeometryLoaderLog, "bufferview has offset: %lld", offset);
357 }
358
359 const quint64 len = json.value(KEY_BYTE_LENGTH).toInt();
360 QByteArray bytes = bufferData.data->mid(index: offset, len);
361 if (Q_UNLIKELY(bytes.count() != int(len))) {
362 qCWarning(GLTFGeometryLoaderLog, "failed to read sufficient bytes from: %ls for view",
363 qUtf16PrintableImpl(bufferData.path));
364 }
365
366 auto b = new Qt3DRender::QBuffer;
367 b->setData(bytes);
368 m_gltf2.m_buffers.push_back(t: b);
369}
370
371void GLTFGeometryLoader::processJSONAccessor(const QString &id, const QJsonObject &json)
372{
373 m_gltf1.m_accessorDict[id] = AccessorData(json);
374}
375
376void GLTFGeometryLoader::processJSONAccessorV2(const QJsonObject &json)
377{
378 m_gltf2.m_accessors.push_back(t: AccessorData(json));
379}
380
381void GLTFGeometryLoader::processJSONMesh(const QString &id, const QJsonObject &json)
382{
383 const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
384 for (const QJsonValue &primitiveValue : primitivesArray) {
385 QJsonObject primitiveObject = primitiveValue.toObject();
386 QString material = primitiveObject.value(KEY_MATERIAL).toString();
387
388 if (Q_UNLIKELY(material.isEmpty())) {
389 qCWarning(GLTFGeometryLoaderLog, "malformed primitive on %ls, missing material value %ls",
390 qUtf16PrintableImpl(id), qUtf16PrintableImpl(material));
391 continue;
392 }
393
394 QGeometry *meshGeometry = new QGeometry;
395
396 const QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject();
397 for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
398 QString k = it.value().toString();
399 const auto accessorIt = qAsConst(t&: m_gltf1.m_accessorDict).find(akey: k);
400 if (Q_UNLIKELY(accessorIt == m_gltf1.m_accessorDict.cend())) {
401 qCWarning(GLTFGeometryLoaderLog, "unknown attribute accessor: %ls on mesh %ls",
402 qUtf16PrintableImpl(k), qUtf16PrintableImpl(id));
403 continue;
404 }
405
406 const QString attrName = it.key();
407 QString attributeName = standardAttributeNameFromSemantic(semantic: attrName);
408 if (attributeName.isEmpty())
409 attributeName = attrName;
410
411 //Get buffer handle for accessor
412 Qt3DRender::QBuffer *buffer = m_gltf1.m_buffers.value(akey: accessorIt->bufferViewName, adefaultValue: nullptr);
413 if (Q_UNLIKELY(!buffer)) {
414 qCWarning(GLTFGeometryLoaderLog, "unknown buffer-view: %ls processing accessor: %ls",
415 qUtf16PrintableImpl(accessorIt->bufferViewName), qUtf16PrintableImpl(id));
416 continue;
417 }
418
419 QAttribute *attribute = new QAttribute(buffer,
420 attributeName,
421 accessorIt->type,
422 accessorIt->dataSize,
423 accessorIt->count,
424 accessorIt->offset,
425 accessorIt->stride);
426 attribute->setAttributeType(QAttribute::VertexAttribute);
427 meshGeometry->addAttribute(attribute);
428 }
429
430 const auto indices = primitiveObject.value(KEY_INDICES);
431 if (!indices.isUndefined()) {
432 QString k = indices.toString();
433 const auto accessorIt = qAsConst(t&: m_gltf1.m_accessorDict).find(akey: k);
434 if (Q_UNLIKELY(accessorIt == m_gltf1.m_accessorDict.cend())) {
435 qCWarning(GLTFGeometryLoaderLog, "unknown index accessor: %ls on mesh %ls",
436 qUtf16PrintableImpl(k), qUtf16PrintableImpl(id));
437 } else {
438 //Get buffer handle for accessor
439 Qt3DRender::QBuffer *buffer = m_gltf1.m_buffers.value(akey: accessorIt->bufferViewName, adefaultValue: nullptr);
440 if (Q_UNLIKELY(!buffer)) {
441 qCWarning(GLTFGeometryLoaderLog, "unknown buffer-view: %ls processing accessor: %ls",
442 qUtf16PrintableImpl(accessorIt->bufferViewName), qUtf16PrintableImpl(id));
443 continue;
444 }
445
446 QAttribute *attribute = new QAttribute(buffer,
447 accessorIt->type,
448 accessorIt->dataSize,
449 accessorIt->count,
450 accessorIt->offset,
451 accessorIt->stride);
452 attribute->setAttributeType(QAttribute::IndexAttribute);
453 meshGeometry->addAttribute(attribute);
454 }
455 } // of has indices
456
457 m_geometry = meshGeometry;
458
459 break;
460 } // of primitives iteration
461}
462
463void GLTFGeometryLoader::processJSONMeshV2(const QJsonObject &json)
464{
465 const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
466 for (const QJsonValue &primitiveValue : primitivesArray) {
467 QJsonObject primitiveObject = primitiveValue.toObject();
468
469 QGeometry *meshGeometry = new QGeometry;
470
471 const QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject();
472 for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
473 const int accessorIndex = it.value().toInt();
474 if (Q_UNLIKELY(accessorIndex >= m_gltf2.m_accessors.size())) {
475 qCWarning(GLTFGeometryLoaderLog, "unknown attribute accessor: %d on mesh %ls",
476 accessorIndex, qUtf16PrintableImpl(json.value(KEY_NAME).toString()));
477 continue;
478 }
479 const auto &accessor = m_gltf2.m_accessors[accessorIndex];
480
481 const QString attrName = it.key();
482 QString attributeName = standardAttributeNameFromSemantic(semantic: attrName);
483 if (attributeName.isEmpty())
484 attributeName = attrName;
485
486 // Get buffer handle for accessor
487 if (Q_UNLIKELY(accessor.bufferViewIndex >= m_gltf2.m_buffers.size())) {
488 qCWarning(GLTFGeometryLoaderLog, "unknown buffer-view: %d processing accessor: %ls",
489 accessor.bufferViewIndex, qUtf16PrintableImpl(json.value(KEY_NAME).toString()));
490 continue;
491 }
492 Qt3DRender::QBuffer *buffer = m_gltf2.m_buffers[accessor.bufferViewIndex];
493
494 QAttribute *attribute = new QAttribute(buffer,
495 attributeName,
496 accessor.type,
497 accessor.dataSize,
498 accessor.count,
499 accessor.offset,
500 accessor.stride);
501 attribute->setAttributeType(QAttribute::VertexAttribute);
502 meshGeometry->addAttribute(attribute);
503 }
504
505 const auto indices = primitiveObject.value(KEY_INDICES);
506 if (!indices.isUndefined()) {
507 const int accessorIndex = indices.toInt();
508 if (Q_UNLIKELY(accessorIndex >= m_gltf2.m_accessors.size())) {
509 qCWarning(GLTFGeometryLoaderLog, "unknown index accessor: %d on mesh %ls",
510 accessorIndex, qUtf16PrintableImpl(json.value(KEY_NAME).toString()));
511 } else {
512 const auto &accessor = m_gltf2.m_accessors[accessorIndex];
513
514 //Get buffer handle for accessor
515 if (Q_UNLIKELY(accessor.bufferViewIndex >= m_gltf2.m_buffers.size())) {
516 qCWarning(GLTFGeometryLoaderLog, "unknown buffer-view: %d processing accessor: %ls",
517 accessor.bufferViewIndex, qUtf16PrintableImpl(json.value(KEY_NAME).toString()));
518 continue;
519 }
520 Qt3DRender::QBuffer *buffer = m_gltf2.m_buffers[accessor.bufferViewIndex];
521
522 QAttribute *attribute = new QAttribute(buffer,
523 accessor.type,
524 accessor.dataSize,
525 accessor.count,
526 accessor.offset,
527 accessor.stride);
528 attribute->setAttributeType(QAttribute::IndexAttribute);
529 meshGeometry->addAttribute(attribute);
530 }
531 } // of has indices
532
533 m_geometry = meshGeometry;
534
535 break;
536 } // of primitives iteration
537}
538
539void GLTFGeometryLoader::loadBufferData()
540{
541 for (auto &bufferData : m_gltf1.m_bufferDatas) {
542 if (!bufferData.data) {
543 bufferData.data = new QByteArray(resolveLocalData(path: bufferData.path));
544 }
545 }
546}
547
548void GLTFGeometryLoader::unloadBufferData()
549{
550 for (const auto &bufferData : qAsConst(t&: m_gltf1.m_bufferDatas)) {
551 QByteArray *data = bufferData.data;
552 delete data;
553 }
554}
555
556void GLTFGeometryLoader::loadBufferDataV2()
557{
558 for (auto &bufferData : m_gltf2.m_bufferDatas) {
559 if (!bufferData.data)
560 bufferData.data = new QByteArray(resolveLocalData(path: bufferData.path));
561 }
562}
563
564void GLTFGeometryLoader::unloadBufferDataV2()
565{
566 for (const auto &bufferData : qAsConst(t&: m_gltf2.m_bufferDatas)) {
567 QByteArray *data = bufferData.data;
568 delete data;
569 }
570}
571
572QByteArray GLTFGeometryLoader::resolveLocalData(const QString &path) const
573{
574 QDir d(m_basePath);
575 Q_ASSERT(d.exists());
576
577 QString absPath = d.absoluteFilePath(fileName: path);
578 QFile f(absPath);
579 f.open(flags: QIODevice::ReadOnly);
580 return f.readAll();
581}
582
583QAttribute::VertexBaseType GLTFGeometryLoader::accessorTypeFromJSON(int componentType)
584{
585 if (componentType == GL_BYTE)
586 return QAttribute::Byte;
587 else if (componentType == GL_UNSIGNED_BYTE)
588 return QAttribute::UnsignedByte;
589 else if (componentType == GL_SHORT)
590 return QAttribute::Short;
591 else if (componentType == GL_UNSIGNED_SHORT)
592 return QAttribute::UnsignedShort;
593 else if (componentType == GL_UNSIGNED_INT)
594 return QAttribute::UnsignedInt;
595 else if (componentType == GL_FLOAT)
596 return QAttribute::Float;
597
598 //There shouldn't be an invalid case here
599 qCWarning(GLTFGeometryLoaderLog, "unsupported accessor type %d", componentType);
600 return QAttribute::Float;
601}
602
603uint GLTFGeometryLoader::accessorDataSizeFromJson(const QString &type)
604{
605 QString typeName = type.toUpper();
606 if (typeName == QLatin1String("SCALAR"))
607 return 1;
608 if (typeName == QLatin1String("VEC2"))
609 return 2;
610 if (typeName == QLatin1String("VEC3"))
611 return 3;
612 if (typeName == QLatin1String("VEC4"))
613 return 4;
614 if (typeName == QLatin1String("MAT2"))
615 return 4;
616 if (typeName == QLatin1String("MAT3"))
617 return 9;
618 if (typeName == QLatin1String("MAT4"))
619 return 16;
620
621 return 0;
622}
623
624} // namespace Qt3DRender
625
626QT_END_NAMESPACE
627

source code of qt3d/src/plugins/geometryloaders/gltf/gltfgeometryloader.cpp