summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMike Krus <mike.krus@kdab.com>2021-06-15 11:32:49 +0100
committerMike Krus <mike.krus@kdab.com>2021-06-20 19:13:46 +0100
commit3c02825fcedbdb0983775d0522af9c851be6c0cd (patch)
tree808549715e7cbc8082ee81ec05617f8e6e5ddf9c /src
parenta6e373ccd2fcbd5133f455c878413584462138fd (diff)
Partially Revert "Remove custom gltf tool"
In b9994cd88925ca012d66e52d033cc9a3a909fc7a, we removed the tool and the parser. This restores the parser, but the tool was unmaintained. Pick-to: 6.1 6.2 Change-Id: I168e720b7fdf65aafebb9652933d8093f5449bdc Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/geometryloaders/CMakeLists.txt1
-rw-r--r--src/plugins/geometryloaders/geometryloaders.pro1
-rw-r--r--src/plugins/geometryloaders/gltf/CMakeLists.txt24
-rw-r--r--src/plugins/geometryloaders/gltf/gltf.json3
-rw-r--r--src/plugins/geometryloaders/gltf/gltf.pro16
-rw-r--r--src/plugins/geometryloaders/gltf/gltfgeometryloader.cpp628
-rw-r--r--src/plugins/geometryloaders/gltf/gltfgeometryloader.h194
-rw-r--r--src/plugins/geometryloaders/gltf/main.cpp71
8 files changed, 938 insertions, 0 deletions
diff --git a/src/plugins/geometryloaders/CMakeLists.txt b/src/plugins/geometryloaders/CMakeLists.txt
index 91944f948..d2ccc5ae5 100644
--- a/src/plugins/geometryloaders/CMakeLists.txt
+++ b/src/plugins/geometryloaders/CMakeLists.txt
@@ -8,6 +8,7 @@ qt_feature_module_begin(
include(configure.cmake)
qt_feature_module_end(NO_MODULE)
+add_subdirectory(gltf)
if(QT_FEATURE_regularexpression)
add_subdirectory(default)
endif()
diff --git a/src/plugins/geometryloaders/geometryloaders.pro b/src/plugins/geometryloaders/geometryloaders.pro
index c462493a4..764c615da 100644
--- a/src/plugins/geometryloaders/geometryloaders.pro
+++ b/src/plugins/geometryloaders/geometryloaders.pro
@@ -1,3 +1,4 @@
TEMPLATE = subdirs
qtConfig(regularexpression) : SUBDIRS += default
+SUBDIRS += gltf
qtConfig(qt3d-fbxsdk) : SUBDIRS += fbx
diff --git a/src/plugins/geometryloaders/gltf/CMakeLists.txt b/src/plugins/geometryloaders/gltf/CMakeLists.txt
new file mode 100644
index 000000000..aa8f80c19
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Generated from gltf.pro.
+
+#####################################################################
+## GLTFGeometryLoaderPlugin Plugin:
+#####################################################################
+
+qt_internal_add_plugin(GLTFGeometryLoaderPlugin
+ OUTPUT_NAME gltfgeometryloader
+ TYPE geometryloaders
+ SOURCES
+ gltfgeometryloader.cpp gltfgeometryloader.h
+ main.cpp
+ PUBLIC_LIBRARIES
+ Qt::3DCore
+ Qt::3DCorePrivate
+ Qt::3DRender
+ Qt::3DRenderPrivate
+ Qt::Core
+ Qt::CorePrivate
+ Qt::Gui
+)
+
+#### Keys ignored in scope 1:.:.:gltf.pro:<TRUE>:
+# DISTFILES = "gltf.json"
diff --git a/src/plugins/geometryloaders/gltf/gltf.json b/src/plugins/geometryloaders/gltf/gltf.json
new file mode 100644
index 000000000..9ad97ce1a
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/gltf.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["gltf", "json", "qgltf"]
+}
diff --git a/src/plugins/geometryloaders/gltf/gltf.pro b/src/plugins/geometryloaders/gltf/gltf.pro
new file mode 100644
index 000000000..815226b4e
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/gltf.pro
@@ -0,0 +1,16 @@
+TARGET = gltfgeometryloader
+QT += core-private 3dcore 3dcore-private 3drender 3drender-private
+
+HEADERS += \
+ gltfgeometryloader.h \
+
+SOURCES += \
+ main.cpp \
+ gltfgeometryloader.cpp \
+
+DISTFILES += \
+ gltf.json
+
+PLUGIN_TYPE = geometryloaders
+PLUGIN_CLASS_NAME = GLTFGeometryLoaderPlugin
+load(qt_plugin)
diff --git a/src/plugins/geometryloaders/gltf/gltfgeometryloader.cpp b/src/plugins/geometryloaders/gltf/gltfgeometryloader.cpp
new file mode 100644
index 000000000..9f13ca2c6
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/gltfgeometryloader.cpp
@@ -0,0 +1,628 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gltfgeometryloader.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonObject>
+#include <QtCore/QVersionNumber>
+
+#include <QOpenGLTexture>
+
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DCore/QGeometry>
+#include <Qt3DCore/private/qloadgltf_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef qUtf16PrintableImpl // -Impl is a Qt 5.8 feature
+# define qUtf16PrintableImpl(string) \
+ static_cast<const wchar_t*>(static_cast<const void*>(string.utf16()))
+#endif
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+
+Q_LOGGING_CATEGORY(GLTFGeometryLoaderLog, "Qt3D.GLTFGeometryLoader", QtWarningMsg)
+
+#define KEY_ASSET QLatin1String("asset")
+#define KEY_ACCESSORS QLatin1String("accessors")
+#define KEY_ATTRIBUTES QLatin1String("attributes")
+#define KEY_BUFFER QLatin1String("buffer")
+#define KEY_BUFFERS QLatin1String("buffers")
+#define KEY_BYTE_LENGTH QLatin1String("byteLength")
+#define KEY_BYTE_OFFSET QLatin1String("byteOffset")
+#define KEY_BYTE_STRIDE QLatin1String("byteStride")
+#define KEY_COUNT QLatin1String("count")
+#define KEY_INDICES QLatin1String("indices")
+#define KEY_MATERIAL QLatin1String("material")
+#define KEY_MESHES QLatin1String("meshes")
+#define KEY_NAME QLatin1String("name")
+#define KEY_PRIMITIVES QLatin1String("primitives")
+#define KEY_TARGET QLatin1String("target")
+#define KEY_TYPE QLatin1String("type")
+#define KEY_URI QLatin1String("uri")
+#define KEY_VERSION QLatin1String("version")
+
+#define KEY_BUFFER_VIEW QLatin1String("bufferView")
+#define KEY_BUFFER_VIEWS QLatin1String("bufferViews")
+#define KEY_COMPONENT_TYPE QLatin1String("componentType")
+
+GLTFGeometryLoader::GLTFGeometryLoader()
+ : m_geometry(nullptr)
+{
+}
+
+GLTFGeometryLoader::~GLTFGeometryLoader()
+{
+ cleanup();
+}
+
+QGeometry *GLTFGeometryLoader::geometry() const
+{
+ return m_geometry;
+}
+
+bool GLTFGeometryLoader::load(QIODevice *ioDev, const QString &subMesh)
+{
+ Q_UNUSED(subMesh);
+
+ if (Q_UNLIKELY(!setJSON(qLoadGLTF(ioDev->readAll())))) {
+ qCWarning(GLTFGeometryLoaderLog, "not a JSON document");
+ return false;
+ }
+
+ auto file = qobject_cast<QFile*>(ioDev);
+ if (file) {
+ QFileInfo finfo(file->fileName());
+ setBasePath(finfo.dir().absolutePath());
+ }
+
+ m_mesh = subMesh;
+
+ parse();
+
+ return true;
+}
+
+GLTFGeometryLoader::BufferData::BufferData()
+ : length(0)
+ , data(nullptr)
+{
+}
+
+GLTFGeometryLoader::BufferData::BufferData(const QJsonObject &json)
+ : length(json.value(KEY_BYTE_LENGTH).toInt())
+ , path(json.value(KEY_URI).toString())
+ , data(nullptr)
+{
+}
+
+GLTFGeometryLoader::AccessorData::AccessorData()
+ : bufferViewIndex(0)
+ , type(QAttribute::Float)
+ , dataSize(0)
+ , count(0)
+ , offset(0)
+ , stride(0)
+{
+
+}
+
+GLTFGeometryLoader::AccessorData::AccessorData(const QJsonObject &json)
+ : bufferViewName(json.value(KEY_BUFFER_VIEW).toString())
+ , bufferViewIndex(json.value(KEY_BUFFER_VIEW).toInt(-1))
+ , type(accessorTypeFromJSON(json.value(KEY_COMPONENT_TYPE).toInt()))
+ , dataSize(accessorDataSizeFromJson(json.value(KEY_TYPE).toString()))
+ , count(json.value(KEY_COUNT).toInt())
+ , offset(0)
+ , stride(0)
+{
+ const auto byteOffset = json.value(KEY_BYTE_OFFSET);
+ if (!byteOffset.isUndefined())
+ offset = byteOffset.toInt();
+ const auto byteStride = json.value(KEY_BYTE_STRIDE);
+ if (!byteStride.isUndefined())
+ stride = byteStride.toInt();
+}
+
+void GLTFGeometryLoader::setBasePath(const QString &path)
+{
+ m_basePath = path;
+}
+
+bool GLTFGeometryLoader::setJSON(const QJsonDocument &json )
+{
+ if (!json.isObject())
+ return false;
+
+ m_json = json;
+
+ cleanup();
+
+ return true;
+}
+
+QString GLTFGeometryLoader::standardAttributeNameFromSemantic(const QString &semantic)
+{
+ //Standard Attributes
+ if (semantic.startsWith(QLatin1String("POSITION")))
+ return QAttribute::defaultPositionAttributeName();
+ if (semantic.startsWith(QLatin1String("NORMAL")))
+ return QAttribute::defaultNormalAttributeName();
+ if (semantic.startsWith(QLatin1String("TEXCOORD")))
+ return QAttribute::defaultTextureCoordinateAttributeName();
+ if (semantic.startsWith(QLatin1String("COLOR")))
+ return QAttribute::defaultColorAttributeName();
+ if (semantic.startsWith(QLatin1String("TANGENT")))
+ return QAttribute::defaultTangentAttributeName();
+ if (semantic.startsWith(QLatin1String("JOINTS")))
+ return QAttribute::defaultJointIndicesAttributeName();
+ if (semantic.startsWith(QLatin1String("WEIGHTS")))
+ return QAttribute::defaultJointWeightsAttributeName();
+
+ return QString();
+}
+
+void GLTFGeometryLoader::parse()
+{
+ // Find the glTF version
+ const QJsonObject asset = m_json.object().value(KEY_ASSET).toObject();
+ const QString versionString = asset.value(KEY_VERSION).toString();
+ const auto version = QVersionNumber::fromString(versionString);
+ switch (version.majorVersion()) {
+ case 1:
+ parseGLTF1();
+ break;
+
+ case 2:
+ parseGLTF2();
+ break;
+
+ default:
+ qWarning() << "Unsupported version of glTF" << versionString;
+ }
+}
+
+void GLTFGeometryLoader::parseGLTF1()
+{
+ const QJsonObject buffers = m_json.object().value(KEY_BUFFERS).toObject();
+ for (auto it = buffers.begin(), end = buffers.end(); it != end; ++it)
+ processJSONBuffer(it.key(), it.value().toObject());
+
+ const QJsonObject views = m_json.object().value(KEY_BUFFER_VIEWS).toObject();
+ loadBufferData();
+ for (auto it = views.begin(), end = views.end(); it != end; ++it)
+ processJSONBufferView(it.key(), it.value().toObject());
+ unloadBufferData();
+
+ const QJsonObject attrs = m_json.object().value(KEY_ACCESSORS).toObject();
+ for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it)
+ processJSONAccessor(it.key(), it.value().toObject());
+
+ const QJsonObject meshes = m_json.object().value(KEY_MESHES).toObject();
+ for (auto it = meshes.begin(), end = meshes.end(); it != end && !m_geometry; ++it) {
+ const QJsonObject &mesh = it.value().toObject();
+ if (m_mesh.isEmpty() ||
+ m_mesh.compare(mesh.value(KEY_NAME).toString(), Qt::CaseInsensitive) == 0)
+ processJSONMesh(it.key(), mesh);
+ }
+}
+
+void GLTFGeometryLoader::parseGLTF2()
+{
+ const QJsonArray buffers = m_json.object().value(KEY_BUFFERS).toArray();
+ for (auto it = buffers.begin(), end = buffers.end(); it != end; ++it)
+ processJSONBufferV2(it->toObject());
+
+ const QJsonArray views = m_json.object().value(KEY_BUFFER_VIEWS).toArray();
+ loadBufferDataV2();
+ for (auto it = views.begin(), end = views.end(); it != end; ++it)
+ processJSONBufferViewV2(it->toObject());
+ unloadBufferDataV2();
+
+ const QJsonArray attrs = m_json.object().value(KEY_ACCESSORS).toArray();
+ for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it)
+ processJSONAccessorV2(it->toObject());
+
+ const QJsonArray meshes = m_json.object().value(KEY_MESHES).toArray();
+ for (auto it = meshes.begin(), end = meshes.end(); it != end && !m_geometry; ++it) {
+ const QJsonObject &mesh = it->toObject();
+ if (m_mesh.isEmpty() || m_mesh.compare(mesh.value(KEY_NAME).toString(), Qt::CaseInsensitive) == 0)
+ processJSONMeshV2(mesh);
+ }
+}
+
+void GLTFGeometryLoader::cleanup()
+{
+ m_geometry = nullptr;
+ m_gltf1.m_accessorDict.clear();
+ m_gltf1.m_buffers.clear();
+}
+
+void GLTFGeometryLoader::processJSONBuffer(const QString &id, const QJsonObject &json)
+{
+ // simply cache buffers for lookup by buffer-views
+ m_gltf1.m_bufferDatas[id] = BufferData(json);
+}
+
+void GLTFGeometryLoader::processJSONBufferV2(const QJsonObject &json)
+{
+ // simply cache buffers for lookup by buffer-views
+ m_gltf2.m_bufferDatas.push_back(BufferData(json));
+}
+
+void GLTFGeometryLoader::processJSONBufferView(const QString &id, const QJsonObject &json)
+{
+ QString bufName = json.value(KEY_BUFFER).toString();
+ const auto it = qAsConst(m_gltf1.m_bufferDatas).find(bufName);
+ if (Q_UNLIKELY(it == m_gltf1.m_bufferDatas.cend())) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown buffer: %ls processing view: %ls",
+ qUtf16PrintableImpl(bufName), qUtf16PrintableImpl(id));
+ return;
+ }
+ const auto &bufferData = *it;
+
+ int target = json.value(KEY_TARGET).toInt();
+
+ switch (target) {
+ case GL_ARRAY_BUFFER:
+ case GL_ELEMENT_ARRAY_BUFFER:
+ break;
+ default:
+ qCWarning(GLTFGeometryLoaderLog, "buffer %ls unsupported target: %d",
+ qUtf16PrintableImpl(id), target);
+ return;
+ }
+
+ quint64 offset = 0;
+ const auto byteOffset = json.value(KEY_BYTE_OFFSET);
+ if (!byteOffset.isUndefined()) {
+ offset = byteOffset.toInt();
+ qCDebug(GLTFGeometryLoaderLog, "bv: %ls has offset: %lld", qUtf16PrintableImpl(id), offset);
+ }
+
+ const quint64 len = json.value(KEY_BYTE_LENGTH).toInt();
+
+ QByteArray bytes = bufferData.data->mid(offset, len);
+ if (Q_UNLIKELY(bytes.count() != int(len))) {
+ qCWarning(GLTFGeometryLoaderLog, "failed to read sufficient bytes from: %ls for view %ls",
+ qUtf16PrintableImpl(bufferData.path), qUtf16PrintableImpl(id));
+ }
+
+ Qt3DCore::QBuffer *b = new Qt3DCore::QBuffer();
+ b->setData(bytes);
+ m_gltf1.m_buffers[id] = b;
+}
+
+void GLTFGeometryLoader::processJSONBufferViewV2(const QJsonObject &json)
+{
+ const int bufferIndex = json.value(KEY_BUFFER).toInt();
+ if (Q_UNLIKELY(bufferIndex) >= m_gltf2.m_bufferDatas.size()) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown buffer: %d processing view", bufferIndex);
+ return;
+ }
+ const auto bufferData = m_gltf2.m_bufferDatas[bufferIndex];
+
+ int target = json.value(KEY_TARGET).toInt();
+ switch (target) {
+ case GL_ARRAY_BUFFER:
+ case GL_ELEMENT_ARRAY_BUFFER:
+ break;
+ default:
+ return;
+ }
+
+ quint64 offset = 0;
+ const auto byteOffset = json.value(KEY_BYTE_OFFSET);
+ if (!byteOffset.isUndefined()) {
+ offset = byteOffset.toInt();
+ qCDebug(GLTFGeometryLoaderLog, "bufferview has offset: %lld", offset);
+ }
+
+ const quint64 len = json.value(KEY_BYTE_LENGTH).toInt();
+ QByteArray bytes = bufferData.data->mid(offset, len);
+ if (Q_UNLIKELY(bytes.count() != int(len))) {
+ qCWarning(GLTFGeometryLoaderLog, "failed to read sufficient bytes from: %ls for view",
+ qUtf16PrintableImpl(bufferData.path));
+ }
+
+ auto b = new Qt3DCore::QBuffer;
+ b->setData(bytes);
+ m_gltf2.m_buffers.push_back(b);
+}
+
+void GLTFGeometryLoader::processJSONAccessor(const QString &id, const QJsonObject &json)
+{
+ m_gltf1.m_accessorDict[id] = AccessorData(json);
+}
+
+void GLTFGeometryLoader::processJSONAccessorV2(const QJsonObject &json)
+{
+ m_gltf2.m_accessors.push_back(AccessorData(json));
+}
+
+void GLTFGeometryLoader::processJSONMesh(const QString &id, const QJsonObject &json)
+{
+ const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
+ for (const QJsonValue &primitiveValue : primitivesArray) {
+ QJsonObject primitiveObject = primitiveValue.toObject();
+ QString material = primitiveObject.value(KEY_MATERIAL).toString();
+
+ if (Q_UNLIKELY(material.isEmpty())) {
+ qCWarning(GLTFGeometryLoaderLog, "malformed primitive on %ls, missing material value %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(material));
+ continue;
+ }
+
+ QGeometry *meshGeometry = new QGeometry;
+
+ const QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject();
+ for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
+ QString k = it.value().toString();
+ const auto accessorIt = qAsConst(m_gltf1.m_accessorDict).find(k);
+ if (Q_UNLIKELY(accessorIt == m_gltf1.m_accessorDict.cend())) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown attribute accessor: %ls on mesh %ls",
+ qUtf16PrintableImpl(k), qUtf16PrintableImpl(id));
+ continue;
+ }
+
+ const QString attrName = it.key();
+ QString attributeName = standardAttributeNameFromSemantic(attrName);
+ if (attributeName.isEmpty())
+ attributeName = attrName;
+
+ //Get buffer handle for accessor
+ Qt3DCore::QBuffer *buffer = m_gltf1.m_buffers.value(accessorIt->bufferViewName, nullptr);
+ if (Q_UNLIKELY(!buffer)) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown buffer-view: %ls processing accessor: %ls",
+ qUtf16PrintableImpl(accessorIt->bufferViewName), qUtf16PrintableImpl(id));
+ continue;
+ }
+
+ QAttribute *attribute = new QAttribute(buffer,
+ attributeName,
+ accessorIt->type,
+ accessorIt->dataSize,
+ accessorIt->count,
+ accessorIt->offset,
+ accessorIt->stride);
+ attribute->setAttributeType(QAttribute::VertexAttribute);
+ meshGeometry->addAttribute(attribute);
+ }
+
+ const auto indices = primitiveObject.value(KEY_INDICES);
+ if (!indices.isUndefined()) {
+ QString k = indices.toString();
+ const auto accessorIt = qAsConst(m_gltf1.m_accessorDict).find(k);
+ if (Q_UNLIKELY(accessorIt == m_gltf1.m_accessorDict.cend())) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown index accessor: %ls on mesh %ls",
+ qUtf16PrintableImpl(k), qUtf16PrintableImpl(id));
+ } else {
+ //Get buffer handle for accessor
+ Qt3DCore::QBuffer *buffer = m_gltf1.m_buffers.value(accessorIt->bufferViewName, nullptr);
+ if (Q_UNLIKELY(!buffer)) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown buffer-view: %ls processing accessor: %ls",
+ qUtf16PrintableImpl(accessorIt->bufferViewName), qUtf16PrintableImpl(id));
+ continue;
+ }
+
+ QAttribute *attribute = new QAttribute(buffer,
+ accessorIt->type,
+ accessorIt->dataSize,
+ accessorIt->count,
+ accessorIt->offset,
+ accessorIt->stride);
+ attribute->setAttributeType(QAttribute::IndexAttribute);
+ meshGeometry->addAttribute(attribute);
+ }
+ } // of has indices
+
+ m_geometry = meshGeometry;
+
+ break;
+ } // of primitives iteration
+}
+
+void GLTFGeometryLoader::processJSONMeshV2(const QJsonObject &json)
+{
+ const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
+ for (const QJsonValue &primitiveValue : primitivesArray) {
+ QJsonObject primitiveObject = primitiveValue.toObject();
+
+ QGeometry *meshGeometry = new QGeometry;
+
+ const QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject();
+ for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
+ const int accessorIndex = it.value().toInt();
+ if (Q_UNLIKELY(accessorIndex >= m_gltf2.m_accessors.size())) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown attribute accessor: %d on mesh %ls",
+ accessorIndex, qUtf16PrintableImpl(json.value(KEY_NAME).toString()));
+ continue;
+ }
+ const auto &accessor = m_gltf2.m_accessors[accessorIndex];
+
+ const QString attrName = it.key();
+ QString attributeName = standardAttributeNameFromSemantic(attrName);
+ if (attributeName.isEmpty())
+ attributeName = attrName;
+
+ // Get buffer handle for accessor
+ if (Q_UNLIKELY(accessor.bufferViewIndex >= m_gltf2.m_buffers.size())) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown buffer-view: %d processing accessor: %ls",
+ accessor.bufferViewIndex, qUtf16PrintableImpl(json.value(KEY_NAME).toString()));
+ continue;
+ }
+ Qt3DCore::QBuffer *buffer = m_gltf2.m_buffers[accessor.bufferViewIndex];
+
+ QAttribute *attribute = new QAttribute(buffer,
+ attributeName,
+ accessor.type,
+ accessor.dataSize,
+ accessor.count,
+ accessor.offset,
+ accessor.stride);
+ attribute->setAttributeType(QAttribute::VertexAttribute);
+ meshGeometry->addAttribute(attribute);
+ }
+
+ const auto indices = primitiveObject.value(KEY_INDICES);
+ if (!indices.isUndefined()) {
+ const int accessorIndex = indices.toInt();
+ if (Q_UNLIKELY(accessorIndex >= m_gltf2.m_accessors.size())) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown index accessor: %d on mesh %ls",
+ accessorIndex, qUtf16PrintableImpl(json.value(KEY_NAME).toString()));
+ } else {
+ const auto &accessor = m_gltf2.m_accessors[accessorIndex];
+
+ //Get buffer handle for accessor
+ if (Q_UNLIKELY(accessor.bufferViewIndex >= m_gltf2.m_buffers.size())) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown buffer-view: %d processing accessor: %ls",
+ accessor.bufferViewIndex, qUtf16PrintableImpl(json.value(KEY_NAME).toString()));
+ continue;
+ }
+ Qt3DCore::QBuffer *buffer = m_gltf2.m_buffers[accessor.bufferViewIndex];
+
+ QAttribute *attribute = new QAttribute(buffer,
+ accessor.type,
+ accessor.dataSize,
+ accessor.count,
+ accessor.offset,
+ accessor.stride);
+ attribute->setAttributeType(QAttribute::IndexAttribute);
+ meshGeometry->addAttribute(attribute);
+ }
+ } // of has indices
+
+ m_geometry = meshGeometry;
+
+ break;
+ } // of primitives iteration
+}
+
+void GLTFGeometryLoader::loadBufferData()
+{
+ for (auto &bufferData : m_gltf1.m_bufferDatas) {
+ if (!bufferData.data) {
+ bufferData.data = new QByteArray(resolveLocalData(bufferData.path));
+ }
+ }
+}
+
+void GLTFGeometryLoader::unloadBufferData()
+{
+ for (const auto &bufferData : qAsConst(m_gltf1.m_bufferDatas)) {
+ QByteArray *data = bufferData.data;
+ delete data;
+ }
+}
+
+void GLTFGeometryLoader::loadBufferDataV2()
+{
+ for (auto &bufferData : m_gltf2.m_bufferDatas) {
+ if (!bufferData.data)
+ bufferData.data = new QByteArray(resolveLocalData(bufferData.path));
+ }
+}
+
+void GLTFGeometryLoader::unloadBufferDataV2()
+{
+ for (const auto &bufferData : qAsConst(m_gltf2.m_bufferDatas)) {
+ QByteArray *data = bufferData.data;
+ delete data;
+ }
+}
+
+QByteArray GLTFGeometryLoader::resolveLocalData(const QString &path) const
+{
+ QDir d(m_basePath);
+ Q_ASSERT(d.exists());
+
+ QString absPath = d.absoluteFilePath(path);
+ QFile f(absPath);
+ f.open(QIODevice::ReadOnly);
+ return f.readAll();
+}
+
+QAttribute::VertexBaseType GLTFGeometryLoader::accessorTypeFromJSON(int componentType)
+{
+ if (componentType == GL_BYTE)
+ return QAttribute::Byte;
+ else if (componentType == GL_UNSIGNED_BYTE)
+ return QAttribute::UnsignedByte;
+ else if (componentType == GL_SHORT)
+ return QAttribute::Short;
+ else if (componentType == GL_UNSIGNED_SHORT)
+ return QAttribute::UnsignedShort;
+ else if (componentType == GL_UNSIGNED_INT)
+ return QAttribute::UnsignedInt;
+ else if (componentType == GL_FLOAT)
+ return QAttribute::Float;
+
+ //There shouldn't be an invalid case here
+ qCWarning(GLTFGeometryLoaderLog, "unsupported accessor type %d", componentType);
+ return QAttribute::Float;
+}
+
+uint GLTFGeometryLoader::accessorDataSizeFromJson(const QString &type)
+{
+ QString typeName = type.toUpper();
+ if (typeName == QLatin1String("SCALAR"))
+ return 1;
+ if (typeName == QLatin1String("VEC2"))
+ return 2;
+ if (typeName == QLatin1String("VEC3"))
+ return 3;
+ if (typeName == QLatin1String("VEC4"))
+ return 4;
+ if (typeName == QLatin1String("MAT2"))
+ return 4;
+ if (typeName == QLatin1String("MAT3"))
+ return 9;
+ if (typeName == QLatin1String("MAT4"))
+ return 16;
+
+ return 0;
+}
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/geometryloaders/gltf/gltfgeometryloader.h b/src/plugins/geometryloaders/gltf/gltfgeometryloader.h
new file mode 100644
index 000000000..36ac0f174
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/gltfgeometryloader.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GLTFGEOMETRYLOADER_H
+#define GLTFGEOMETRYLOADER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QJsonDocument>
+
+#include <Qt3DRender/private/qgeometryloaderinterface_p.h>
+#include <Qt3DCore/qattribute.h>
+#include <Qt3DCore/qbuffer.h>
+
+#include <private/qlocale_tools_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+class QGeometry;
+}
+
+namespace Qt3DRender {
+
+#define GLTFGEOMETRYLOADER_EXT QLatin1String("gltf")
+#define JSONGEOMETRYLOADER_EXT QLatin1String("json")
+#define QGLTFGEOMETRYLOADER_EXT QLatin1String("qgltf")
+
+class QCamera;
+class QCameraLens;
+class QMaterial;
+class QShaderProgram;
+class QEffect;
+class QAbstractTexture;
+class QRenderState;
+class QTechnique;
+class QParameter;
+class QGeometryRenderer;
+
+class GLTFGeometryLoader : public QGeometryLoaderInterface
+{
+ class BufferData
+ {
+ public:
+ BufferData();
+ explicit BufferData(const QJsonObject &json);
+
+ quint64 length;
+ QString path;
+ QByteArray *data;
+ // type if ever useful
+ };
+
+ class ParameterData
+ {
+ public:
+ ParameterData();
+ explicit ParameterData(const QJsonObject &json);
+
+ QString semantic;
+ int type;
+ };
+
+ class AccessorData
+ {
+ public:
+ AccessorData();
+ explicit AccessorData(const QJsonObject &json);
+
+ QString bufferViewName;
+ int bufferViewIndex;
+ Qt3DCore::QAttribute::VertexBaseType type;
+ uint dataSize;
+ int count;
+ int offset;
+ int stride;
+ };
+
+ struct Gltf1
+ {
+ QHash<QString, AccessorData> m_accessorDict;
+ QHash<QString, BufferData> m_bufferDatas;
+ QHash<QString, Qt3DCore::QBuffer*> m_buffers;
+ };
+
+ struct Gltf2
+ {
+ QVector<BufferData> m_bufferDatas;
+ QVector<Qt3DCore::QBuffer*> m_buffers;
+ QVector<AccessorData> m_accessors;
+ };
+
+ Q_OBJECT
+public:
+ GLTFGeometryLoader();
+ ~GLTFGeometryLoader();
+
+ Qt3DCore::QGeometry *geometry() const final;
+
+ bool load(QIODevice *ioDev, const QString &subMesh = QString()) final;
+
+protected:
+ void setBasePath(const QString &path);
+ bool setJSON(const QJsonDocument &json);
+
+ static QString standardAttributeNameFromSemantic(const QString &semantic);
+
+ void parse();
+ void parseGLTF1();
+ void parseGLTF2();
+ void cleanup();
+
+ void processJSONBuffer(const QString &id, const QJsonObject &json);
+ void processJSONBufferView(const QString &id, const QJsonObject &json);
+ void processJSONAccessor(const QString &id, const QJsonObject &json);
+ void processJSONMesh(const QString &id, const QJsonObject &json);
+
+ void loadBufferData();
+ void unloadBufferData();
+
+ void processJSONBufferV2(const QJsonObject &json);
+ void processJSONBufferViewV2(const QJsonObject &json);
+ void processJSONAccessorV2(const QJsonObject &json);
+ void processJSONMeshV2(const QJsonObject &json);
+
+ void loadBufferDataV2();
+ void unloadBufferDataV2();
+
+ QByteArray resolveLocalData(const QString &path) const;
+
+ static Qt3DCore::QAttribute::VertexBaseType accessorTypeFromJSON(int componentType);
+ static uint accessorDataSizeFromJson(const QString &type);
+
+private:
+ QJsonDocument m_json;
+ QString m_basePath;
+ QString m_mesh;
+
+ Gltf1 m_gltf1;
+ Gltf2 m_gltf2;
+
+ Qt3DCore::QGeometry *m_geometry;
+};
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // GLTFGEOMETRYLOADER_H
diff --git a/src/plugins/geometryloaders/gltf/main.cpp b/src/plugins/geometryloaders/gltf/main.cpp
new file mode 100644
index 000000000..ee27e1325
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/main.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <Qt3DRender/private/qgeometryloaderfactory_p.h>
+
+#include "gltfgeometryloader.h"
+
+QT_BEGIN_NAMESPACE
+
+class GLTFGeometryLoaderPlugin : public Qt3DRender::QGeometryLoaderFactory
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QGeometryLoaderFactory_iid FILE "gltf.json")
+public:
+
+ QStringList keys() const override
+ {
+ return QStringList() << GLTFGEOMETRYLOADER_EXT
+ << JSONGEOMETRYLOADER_EXT
+ << QGLTFGEOMETRYLOADER_EXT;
+ }
+
+ Qt3DRender::QGeometryLoaderInterface *create(const QString &ext) override
+ {
+ if ((ext.compare(GLTFGEOMETRYLOADER_EXT, Qt::CaseInsensitive) == 0) ||
+ (ext.compare(JSONGEOMETRYLOADER_EXT, Qt::CaseInsensitive) == 0) ||
+ (ext.compare(QGLTFGEOMETRYLOADER_EXT, Qt::CaseInsensitive) == 0))
+ return new Qt3DRender::GLTFGeometryLoader;
+ return nullptr;
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "main.moc"