summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuillermo A. Amaral <gamaral@kdab.com>2016-10-12 13:26:47 -0700
committerSean Harmer <sean.harmer@kdab.com>2017-01-20 13:56:32 +0000
commit91ca1f782806aab4e916c85836eff52158e61939 (patch)
treeb5499660c8e61138212f77652f0f64cb106d7715
parentb97213a2575b16717e1d7a0ca8235b8884a240bd (diff)
Added GLTF geometry loader
Based on scene parser. Change-Id: I9e4a84926fffaf9e2fa69ac351cfa151b3e79e95 Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
-rw-r--r--src/plugins/geometryloaders/geometryloaders.pro1
-rw-r--r--src/plugins/geometryloaders/gltf/gltf.json3
-rw-r--r--src/plugins/geometryloaders/gltf/gltf.pro19
-rw-r--r--src/plugins/geometryloaders/gltf/gltfgeometryloader.cpp439
-rw-r--r--src/plugins/geometryloaders/gltf/gltfgeometryloader.h168
-rw-r--r--src/plugins/geometryloaders/gltf/main.cpp71
-rw-r--r--tests/auto/render/geometryloaders/cube.gltf263
-rw-r--r--tests/auto/render/geometryloaders/cube_buffer.binbin0 -> 648 bytes
-rw-r--r--tests/auto/render/geometryloaders/geometryloaders.qrc2
-rw-r--r--tests/auto/render/geometryloaders/tst_geometryloaders.cpp40
10 files changed, 1006 insertions, 0 deletions
diff --git a/src/plugins/geometryloaders/geometryloaders.pro b/src/plugins/geometryloaders/geometryloaders.pro
index 6f4b913cb..85ab48d01 100644
--- a/src/plugins/geometryloaders/geometryloaders.pro
+++ b/src/plugins/geometryloaders/geometryloaders.pro
@@ -1,2 +1,3 @@
TEMPLATE = subdirs
SUBDIRS += default
+SUBDIRS += gltf
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..da805fae1
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/gltf.pro
@@ -0,0 +1,19 @@
+TARGET = gltfgeometryloader
+QT += core-private 3dcore 3dcore-private 3drender 3drender-private
+
+# Qt3D is free of Q_FOREACH - make sure it stays that way:
+DEFINES += QT_NO_FOREACH
+
+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..bc64ae41b
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/gltfgeometryloader.cpp
@@ -0,0 +1,439 @@
+/****************************************************************************
+**
+** 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 <QtGui/QOpenGLTexture>
+
+#include <Qt3DRender/QGeometry>
+#include <Qt3DRender/private/renderlogging_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
+
+namespace Qt3DRender {
+
+Q_LOGGING_CATEGORY(GLTFGeometryLoaderLog, "Qt3D.GLTFGeometryLoader")
+
+#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_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);
+
+ QByteArray jsonData = ioDev->readAll();
+ QJsonDocument sceneDocument = QJsonDocument::fromBinaryData(jsonData);
+ if (sceneDocument.isNull())
+ sceneDocument = QJsonDocument::fromJson(jsonData);
+
+ if (Q_UNLIKELY(!setJSON(sceneDocument))) {
+ 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()
+ : type(QAttribute::Float)
+ , dataSize(0)
+ , count(0)
+ , offset(0)
+ , stride(0)
+{
+
+}
+
+GLTFGeometryLoader::AccessorData::AccessorData(const QJsonObject &json)
+ : bufferViewName(json.value(KEY_BUFFER_VIEW).toString())
+ , 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();
+
+ return QString();
+}
+
+void GLTFGeometryLoader::parse()
+{
+ 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::cleanup()
+{
+ m_geometry = nullptr;
+ m_accessorDict.clear();
+ m_buffers.clear();
+}
+
+void GLTFGeometryLoader::processJSONBuffer(const QString &id, const QJsonObject &json)
+{
+ // simply cache buffers for lookup by buffer-views
+ m_bufferDatas[id] = BufferData(json);
+}
+
+void GLTFGeometryLoader::processJSONBufferView(const QString &id, const QJsonObject &json)
+{
+ QString bufName = json.value(KEY_BUFFER).toString();
+ const auto it = qAsConst(m_bufferDatas).find(bufName);
+ if (Q_UNLIKELY(it == 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();
+ Qt3DRender::QBuffer::BufferType ty(Qt3DRender::QBuffer::VertexBuffer);
+
+ switch (target) {
+ case GL_ARRAY_BUFFER: ty = Qt3DRender::QBuffer::VertexBuffer; break;
+ case GL_ELEMENT_ARRAY_BUFFER: ty = Qt3DRender::QBuffer::IndexBuffer; 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);
+ }
+
+ 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));
+ }
+
+ Qt3DRender::QBuffer *b(new Qt3DRender::QBuffer(ty));
+ b->setData(bytes);
+ m_buffers[id] = b;
+}
+
+void GLTFGeometryLoader::processJSONAccessor(const QString &id, const QJsonObject &json)
+{
+ m_accessorDict[id] = 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_accessorDict).find(k);
+ if (Q_UNLIKELY(accessorIt == 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
+ Qt3DRender::QBuffer *buffer = 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_accessorDict).find(k);
+ if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) {
+ qCWarning(GLTFGeometryLoaderLog, "unknown index accessor: %ls on mesh %ls",
+ qUtf16PrintableImpl(k), qUtf16PrintableImpl(id));
+ } else {
+ //Get buffer handle for accessor
+ Qt3DRender::QBuffer *buffer = 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::loadBufferData()
+{
+ for (auto &bufferData : m_bufferDatas) {
+ if (!bufferData.data) {
+ bufferData.data = new QByteArray(resolveLocalData(bufferData.path));
+ }
+ }
+}
+
+void GLTFGeometryLoader::unloadBufferData()
+{
+ for (const auto &bufferData : qAsConst(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..b2245b8ab
--- /dev/null
+++ b/src/plugins/geometryloaders/gltf/gltfgeometryloader.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** 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 <Qt3DRender/qattribute.h>
+#include <Qt3DRender/qbuffer.h>
+
+#include <private/qlocale_tools_p.h>
+
+QT_BEGIN_NAMESPACE
+
+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 QGeometry;
+
+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;
+ QAttribute::VertexBaseType type;
+ uint dataSize;
+ int count;
+ int offset;
+ int stride;
+ };
+
+ Q_OBJECT
+public:
+ GLTFGeometryLoader();
+ ~GLTFGeometryLoader();
+
+ QGeometry *geometry() const Q_DECL_FINAL;
+
+ bool load(QIODevice *ioDev, const QString &subMesh = QString()) Q_DECL_FINAL;
+
+protected:
+ void setBasePath(const QString &path);
+ bool setJSON(const QJsonDocument &json);
+
+ static QString standardAttributeNameFromSemantic(const QString &semantic);
+
+ void parse();
+ 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();
+
+ QByteArray resolveLocalData(const QString &path) const;
+
+ static QAttribute::VertexBaseType accessorTypeFromJSON(int componentType);
+ static uint accessorDataSizeFromJson(const QString &type);
+
+private:
+ QJsonDocument m_json;
+ QString m_basePath;
+ QString m_mesh;
+
+ QHash<QString, AccessorData> m_accessorDict;
+
+ QHash<QString, BufferData> m_bufferDatas;
+ QHash<QString, Qt3DRender::QBuffer*> m_buffers;
+
+ 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..71289368a
--- /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 Q_DECL_OVERRIDE
+ {
+ return QStringList() << GLTFGEOMETRYLOADER_EXT
+ << JSONGEOMETRYLOADER_EXT
+ << QGLTFGEOMETRYLOADER_EXT;
+ }
+
+ Qt3DRender::QGeometryLoaderInterface *create(const QString &ext) Q_DECL_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"
diff --git a/tests/auto/render/geometryloaders/cube.gltf b/tests/auto/render/geometryloaders/cube.gltf
new file mode 100644
index 000000000..c60ae4ad9
--- /dev/null
+++ b/tests/auto/render/geometryloaders/cube.gltf
@@ -0,0 +1,263 @@
+{
+ "accessors": {
+ "accessor_index_0": {
+ "bufferView": "bufferView_1",
+ "byteOffset": 0,
+ "byteStride": 0,
+ "componentType": 5123,
+ "count": 36,
+ "type": "SCALAR",
+ "min": [
+ 0
+ ],
+ "max": [
+ 23
+ ]
+ },
+ "accessor_position": {
+ "bufferView": "bufferView_0",
+ "byteOffset": 0,
+ "byteStride": 0,
+ "componentType": 5126,
+ "count": 24,
+ "min": [
+ -1,
+ -1,
+ -1
+ ],
+ "max": [
+ 1,
+ 1,
+ 1.0000009536743164
+ ],
+ "type": "VEC3"
+ },
+ "accessor_normal": {
+ "bufferView": "bufferView_0",
+ "byteOffset": 288,
+ "byteStride": 0,
+ "componentType": 5126,
+ "count": 24,
+ "type": "VEC3",
+ "min": [
+ -1,
+ -1,
+ -1
+ ],
+ "max": [
+ 1,
+ 1,
+ 1
+ ]
+ }
+ },
+ "asset": {
+ "generator": "OBJ2GLTF",
+ "premultipliedAlpha": true,
+ "profile": {
+ "api": "WebGL",
+ "version": "1.0"
+ },
+ "version": "1.0"
+ },
+ "buffers": {
+ "cube_buffer": {
+ "type": "arraybuffer",
+ "byteLength": 648,
+ "uri": "cube_buffer.bin"
+ }
+ },
+ "bufferViews": {
+ "bufferView_0": {
+ "buffer": "cube_buffer",
+ "byteLength": 576,
+ "byteOffset": 0,
+ "target": 34962
+ },
+ "bufferView_1": {
+ "buffer": "cube_buffer",
+ "byteLength": 72,
+ "byteOffset": 576,
+ "target": 34963
+ }
+ },
+ "images": {},
+ "materials": {
+ "material_czmDefaultMat": {
+ "name": "czmDefaultMat",
+ "extensions": {},
+ "values": {
+ "ambient": [
+ 0,
+ 0,
+ 0,
+ 1
+ ],
+ "diffuse": [
+ 0.5,
+ 0.5,
+ 0.5,
+ 1
+ ],
+ "emission": [
+ 0,
+ 0,
+ 0,
+ 1
+ ],
+ "specular": [
+ 0,
+ 0,
+ 0,
+ 1
+ ],
+ "shininess": 0,
+ "transparency": 1
+ },
+ "technique": "technique0"
+ }
+ },
+ "meshes": {
+ "mesh_cube": {
+ "name": "cube",
+ "primitives": [
+ {
+ "attributes": {
+ "POSITION": "accessor_position",
+ "NORMAL": "accessor_normal"
+ },
+ "indices": "accessor_index_0",
+ "material": "material_czmDefaultMat",
+ "mode": 4
+ }
+ ]
+ }
+ },
+ "nodes": {
+ "rootNode": {
+ "children": [],
+ "meshes": [
+ "mesh_cube"
+ ],
+ "matrix": [
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1
+ ]
+ }
+ },
+ "samplers": {},
+ "scene": "scene_cube",
+ "scenes": {
+ "scene_cube": {
+ "nodes": [
+ "rootNode"
+ ]
+ }
+ },
+ "textures": {},
+ "extensionsUsed": [],
+ "animations": {},
+ "cameras": {},
+ "techniques": {
+ "technique0": {
+ "attributes": {
+ "a_position": "position",
+ "a_normal": "normal"
+ },
+ "parameters": {
+ "modelViewMatrix": {
+ "semantic": "MODELVIEW",
+ "type": 35676
+ },
+ "projectionMatrix": {
+ "semantic": "PROJECTION",
+ "type": 35676
+ },
+ "normalMatrix": {
+ "semantic": "MODELVIEWINVERSETRANSPOSE",
+ "type": 35675
+ },
+ "ambient": {
+ "type": 35666
+ },
+ "diffuse": {
+ "type": 35666
+ },
+ "emission": {
+ "type": 35666
+ },
+ "specular": {
+ "type": 35666
+ },
+ "shininess": {
+ "type": 5126
+ },
+ "transparency": {
+ "type": 5126
+ },
+ "position": {
+ "semantic": "POSITION",
+ "type": 35665
+ },
+ "normal": {
+ "semantic": "NORMAL",
+ "type": 35665
+ }
+ },
+ "program": "program0",
+ "states": {
+ "enable": [
+ 2884,
+ 2929
+ ]
+ },
+ "uniforms": {
+ "u_modelViewMatrix": "modelViewMatrix",
+ "u_projectionMatrix": "projectionMatrix",
+ "u_normalMatrix": "normalMatrix",
+ "u_ambient": "ambient",
+ "u_diffuse": "diffuse",
+ "u_emission": "emission",
+ "u_specular": "specular",
+ "u_shininess": "shininess",
+ "u_transparency": "transparency"
+ }
+ }
+ },
+ "programs": {
+ "program0": {
+ "attributes": [
+ "a_position",
+ "a_normal"
+ ],
+ "fragmentShader": "fragmentShader0",
+ "vertexShader": "vertexShader0"
+ }
+ },
+ "shaders": {
+ "vertexShader0": {
+ "type": 35633,
+ "uri": "vertexShader0.glsl"
+ },
+ "fragmentShader0": {
+ "type": 35632,
+ "uri": "fragmentShader0.glsl"
+ }
+ },
+ "skins": {},
+ "extensions": {}
+}
diff --git a/tests/auto/render/geometryloaders/cube_buffer.bin b/tests/auto/render/geometryloaders/cube_buffer.bin
new file mode 100644
index 000000000..78e4588be
--- /dev/null
+++ b/tests/auto/render/geometryloaders/cube_buffer.bin
Binary files differ
diff --git a/tests/auto/render/geometryloaders/geometryloaders.qrc b/tests/auto/render/geometryloaders/geometryloaders.qrc
index a2418d2fd..f052420d5 100644
--- a/tests/auto/render/geometryloaders/geometryloaders.qrc
+++ b/tests/auto/render/geometryloaders/geometryloaders.qrc
@@ -3,5 +3,7 @@
<file>cube.obj</file>
<file>cube.ply</file>
<file>cube.stl</file>
+ <file>cube.gltf</file>
+ <file>cube_buffer.bin</file>
</qresource>
</RCC>
diff --git a/tests/auto/render/geometryloaders/tst_geometryloaders.cpp b/tests/auto/render/geometryloaders/tst_geometryloaders.cpp
index 61910c417..03c3abc31 100644
--- a/tests/auto/render/geometryloaders/tst_geometryloaders.cpp
+++ b/tests/auto/render/geometryloaders/tst_geometryloaders.cpp
@@ -61,6 +61,7 @@ private Q_SLOTS:
void testOBJLoader();
void testPLYLoader();
void testSTLLoader();
+ void testGLTFLoader();
};
void tst_geometryloaders::testOBJLoader()
@@ -173,6 +174,45 @@ void tst_geometryloaders::testSTLLoader()
file.close();
}
+void tst_geometryloaders::testGLTFLoader()
+{
+ QScopedPointer<QGeometryLoaderInterface> loader;
+ loader.reset(qLoadPlugin<QGeometryLoaderInterface, QGeometryLoaderFactory>(geometryLoader(), QStringLiteral("gltf")));
+ QVERIFY(loader);
+ if (!loader)
+ return;
+
+ QFile file(QStringLiteral(":/cube.gltf"));
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qDebug("Could not open test file for reading");
+ return;
+ }
+
+ bool loaded = loader->load(&file, QStringLiteral("Cube"));
+ QVERIFY(loaded);
+ if (!loaded)
+ return;
+
+ QGeometry *geometry = loader->geometry();
+ QVERIFY(geometry);
+ if (!geometry)
+ return;
+
+ QCOMPARE(geometry->attributes().count(), 3);
+ for (QAttribute *attr : geometry->attributes()) {
+ switch (attr->attributeType()) {
+ case QAttribute::IndexAttribute:
+ QCOMPARE(attr->count(), 36u);
+ break;
+ case QAttribute::VertexAttribute:
+ QCOMPARE(attr->count(), 24u);
+ break;
+ }
+ }
+
+ file.close();
+}
+
QTEST_MAIN(tst_geometryloaders)
#include "tst_geometryloaders.moc"