summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--tests/auto/render/geometryloaders/CMakeLists.txt1
-rw-r--r--tests/auto/render/geometryloaders/cube.gltf263
-rw-r--r--tests/auto/render/geometryloaders/geometryloaders.qrc1
-rw-r--r--tests/auto/render/geometryloaders/tst_geometryloaders.cpp43
12 files changed, 1246 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"
diff --git a/tests/auto/render/geometryloaders/CMakeLists.txt b/tests/auto/render/geometryloaders/CMakeLists.txt
index 1897c27b1..251c1ae03 100644
--- a/tests/auto/render/geometryloaders/CMakeLists.txt
+++ b/tests/auto/render/geometryloaders/CMakeLists.txt
@@ -19,6 +19,7 @@ qt_internal_add_test(tst_geometryloaders
# Resources:
set(geometryloaders_resource_files
"cube.fbx"
+ "cube.gltf"
"cube.obj"
"cube.ply"
"cube.stl"
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/geometryloaders.qrc b/tests/auto/render/geometryloaders/geometryloaders.qrc
index a15332941..8f98f5a14 100644
--- a/tests/auto/render/geometryloaders/geometryloaders.qrc
+++ b/tests/auto/render/geometryloaders/geometryloaders.qrc
@@ -4,6 +4,7 @@
<file>cube2.obj</file>
<file>cube.ply</file>
<file>cube.stl</file>
+ <file>cube.gltf</file>
<file>cube_buffer.bin</file>
<file>cube.fbx</file>
</qresource>
diff --git a/tests/auto/render/geometryloaders/tst_geometryloaders.cpp b/tests/auto/render/geometryloaders/tst_geometryloaders.cpp
index e0141a51d..7fc914cda 100644
--- a/tests/auto/render/geometryloaders/tst_geometryloaders.cpp
+++ b/tests/auto/render/geometryloaders/tst_geometryloaders.cpp
@@ -65,6 +65,7 @@ private Q_SLOTS:
void testOBJLoader();
void testPLYLoader();
void testSTLLoader();
+ void testGLTFLoader();
#ifdef QT_3DGEOMETRYLOADERS_FBX
void testFBXLoader();
#endif
@@ -193,6 +194,48 @@ 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;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ file.close();
+}
+
#ifdef QT_3DGEOMETRYLOADERS_FBX
void tst_geometryloaders::testFBXLoader()
{