diff options
author | Guillermo A. Amaral <gamaral@kdab.com> | 2016-10-12 13:26:47 -0700 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2017-01-20 13:56:32 +0000 |
commit | 91ca1f782806aab4e916c85836eff52158e61939 (patch) | |
tree | b5499660c8e61138212f77652f0f64cb106d7715 | |
parent | b97213a2575b16717e1d7a0ca8235b8884a240bd (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.pro | 1 | ||||
-rw-r--r-- | src/plugins/geometryloaders/gltf/gltf.json | 3 | ||||
-rw-r--r-- | src/plugins/geometryloaders/gltf/gltf.pro | 19 | ||||
-rw-r--r-- | src/plugins/geometryloaders/gltf/gltfgeometryloader.cpp | 439 | ||||
-rw-r--r-- | src/plugins/geometryloaders/gltf/gltfgeometryloader.h | 168 | ||||
-rw-r--r-- | src/plugins/geometryloaders/gltf/main.cpp | 71 | ||||
-rw-r--r-- | tests/auto/render/geometryloaders/cube.gltf | 263 | ||||
-rw-r--r-- | tests/auto/render/geometryloaders/cube_buffer.bin | bin | 0 -> 648 bytes | |||
-rw-r--r-- | tests/auto/render/geometryloaders/geometryloaders.qrc | 2 | ||||
-rw-r--r-- | tests/auto/render/geometryloaders/tst_geometryloaders.cpp | 40 |
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 Binary files differnew file mode 100644 index 000000000..78e4588be --- /dev/null +++ b/tests/auto/render/geometryloaders/cube_buffer.bin 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" |