summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorAndy Nichols <andy.nichols@theqtcompany.com>2015-07-15 23:39:58 +0200
committerAndy Nichols <andy.nichols@theqtcompany.com>2015-08-08 12:55:31 +0000
commit473bd13ed373c4cc24bdd3211ee90a070a466ce2 (patch)
tree4dbfd0bc5ea908a3feb20fed8ad5242c6cef56bb /src/plugins
parenta1a7e3661bce196a633ef3edd7d905c215bf9031 (diff)
Add support for loading scenes from glTF 0.8 files
This patch includes a new scene parser plugin which enables the loading of glTF files (using the 0.8 spec) into Qt3D scenes. The gltf example has been updated to work with the current APIs and the asset files it used have been updated to match the glTF 0.8 spec requrements. Previously an unused copy of the GLTF scene parser was located in render/io. It was being built, but not used as only plugin based sceneloaders were loaded by the renderer. Now it should be usable and always available. Change-Id: Ic2e31e2b63a871559aad9bad90ec2820988a1571 Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/sceneparsers/gltf/gltf.pro12
-rw-r--r--src/plugins/sceneparsers/gltf/gltfparser.cpp1345
-rw-r--r--src/plugins/sceneparsers/gltf/gltfparser_p.h194
-rw-r--r--src/plugins/sceneparsers/sceneparsers.pro2
4 files changed, 1553 insertions, 0 deletions
diff --git a/src/plugins/sceneparsers/gltf/gltf.pro b/src/plugins/sceneparsers/gltf/gltf.pro
new file mode 100644
index 000000000..bf289372d
--- /dev/null
+++ b/src/plugins/sceneparsers/gltf/gltf.pro
@@ -0,0 +1,12 @@
+TARGET = gltfsceneparser
+QT += core-private 3dcore 3dcore-private 3drenderer 3drenderer-private
+
+PLUGIN_TYPE = sceneparsers
+PLUGIN_CLASS_NAME = GLTFParser
+load(qt_plugin)
+
+HEADERS += \
+ gltfparser_p.h
+
+SOURCES += \
+ gltfparser.cpp
diff --git a/src/plugins/sceneparsers/gltf/gltfparser.cpp b/src/plugins/sceneparsers/gltf/gltfparser.cpp
new file mode 100644
index 000000000..a4a0602c2
--- /dev/null
+++ b/src/plugins/sceneparsers/gltf/gltfparser.cpp
@@ -0,0 +1,1345 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gltfparser_p.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonObject>
+
+#include <QtGui/QVector2D>
+
+#include <Qt3DCore/QCameraLens>
+#include <Qt3DCore/QEntity>
+#include <Qt3DCore/QMatrixTransform>
+#include <Qt3DCore/QTransform>
+
+#include <Qt3DCore/private/qurlhelper_p.h>
+
+#include <Qt3DRenderer/QAlphaCoverage>
+#include <Qt3DRenderer/QBlendEquation>
+#include <Qt3DRenderer/QBlendStateSeparate>
+#include <Qt3DRenderer/QColorMask>
+#include <Qt3DRenderer/QCullFace>
+#include <Qt3DRenderer/QDepthMask>
+#include <Qt3DRenderer/QDepthTest>
+#include <Qt3DRenderer/QEffect>
+#include <Qt3DRenderer/QFrontFace>
+#include <Qt3DRenderer/QGeometry>
+#include <Qt3DRenderer/QGeometryRenderer>
+#include <Qt3DRenderer/QMaterial>
+#include <Qt3DRenderer/QParameter>
+#include <Qt3DRenderer/QParameterMapping>
+#include <Qt3DRenderer/QPolygonOffset>
+#include <Qt3DRenderer/QRenderState>
+#include <Qt3DRenderer/QScissorTest>
+#include <Qt3DRenderer/QShaderProgram>
+#include <Qt3DRenderer/QTechnique>
+#include <Qt3DRenderer/QTexture>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3D {
+
+Q_LOGGING_CATEGORY(GLTFParserLog, "Qt3D.GLTFParser")
+
+namespace {
+
+const QString KEY_CAMERA = QStringLiteral("camera");
+const QString KEY_CAMERAS = QStringLiteral("cameras");
+const QString KEY_SCENES = QStringLiteral("scenes");
+const QString KEY_NODES = QStringLiteral("nodes");
+const QString KEY_MESHES = QStringLiteral("meshes");
+const QString KEY_CHILDREN = QStringLiteral("children");
+const QString KEY_MATRIX = QStringLiteral("matrix");
+const QString KEY_TYPE = QStringLiteral("type");
+const QString KEY_PERSPECTIVE =QStringLiteral("perspective");
+const QString KEY_NAME = QStringLiteral("name");
+const QString KEY_COUNT = QStringLiteral("count");
+const QString KEY_YFOV = QStringLiteral("yfov");
+const QString KEY_ZNEAR = QStringLiteral("znear");
+const QString KEY_ZFAR = QStringLiteral("zfar");
+const QString KEY_MATERIALS = QStringLiteral("materials");
+const QString KEY_TECHNIQUE = QStringLiteral("technique");
+const QString KEY_VALUES = QStringLiteral("values");
+const QString KEY_BUFFERS = QStringLiteral("buffers");
+const QString KEY_SHADERS = QStringLiteral("shaders");
+const QString KEY_PROGRAMS = QStringLiteral("programs");
+const QString KEY_PROGRAM = QStringLiteral("program");
+const QString KEY_TECHNIQUES = QStringLiteral("techniques");
+const QString KEY_ACCESSORS = QStringLiteral("accessors");
+const QString KEY_IMAGES = QStringLiteral("images");
+const QString KEY_TEXTURES = QStringLiteral("textures");
+const QString KEY_SCENE = QStringLiteral("scene");
+const QString KEY_BUFFER = QStringLiteral("buffer");
+const QString KEY_TARGET = QStringLiteral("target");
+const QString KEY_BYTE_OFFSET = QStringLiteral("byteOffset");
+const QString KEY_BYTE_LENGTH = QStringLiteral("byteLength");
+const QString KEY_BYTE_STRIDE = QStringLiteral("byteStride");
+const QString KEY_PRIMITIVES = QStringLiteral("primitives");
+const QString KEY_PRIMITIVE = QStringLiteral("primitive");
+const QString KEY_MATERIAL = QStringLiteral("material");
+const QString KEY_ATTRIBUTES = QStringLiteral("attributes");
+const QString KEY_INDICES = QStringLiteral("indices");
+const QString KEY_URI = QStringLiteral("uri");
+const QString KEY_FORMAT = QStringLiteral("format");
+const QString KEY_PASSES = QStringLiteral("passes");
+const QString KEY_SOURCE = QStringLiteral("source");
+const QString KEY_SAMPLER = QStringLiteral("sampler");
+const QString KEY_SAMPLERS = QStringLiteral("samplers");
+const QString KEY_SEMANTIC = QStringLiteral("semantic");
+const QString KEY_STATES = QStringLiteral("states");
+const QString KEY_UNIFORMS = QStringLiteral("uniforms");
+const QString KEY_PARAMETERS = QStringLiteral("parameters");
+const QString KEY_WRAP_S = QStringLiteral("wrapS");
+const QString KEY_MIN_FILTER = QStringLiteral("minFilter");
+const QString KEY_MAG_FILTER = QStringLiteral("magFilter");
+
+const QString KEY_INSTANCE_TECHNIQUE = QStringLiteral("instanceTechnique");
+const QString KEY_INSTANCE_PROGRAM = QStringLiteral("instanceProgram");
+const QString KEY_BUFFER_VIEWS = QStringLiteral("bufferViews");
+const QString KEY_BUFFER_VIEW = QStringLiteral("bufferView");
+const QString KEY_VERTEX_SHADER = QStringLiteral("vertexShader");
+const QString KEY_FRAGMENT_SHADER = QStringLiteral("fragmentShader");
+const QString KEY_INTERNAL_FORMAT = QStringLiteral("internalFormat");
+const QString KEY_COMPONENT_TYPE = QStringLiteral("componentType");
+const QString KEY_ASPECT_RATIO = QStringLiteral("aspect_ratio");
+const QString KEY_VALUE = QStringLiteral("value");
+const QString KEY_ENABLE = QStringLiteral("enable");
+const QString KEY_FUNCTIONS = QStringLiteral("functions");
+
+} // of anonymous namespace
+
+GLTFParser::GLTFParser() : AbstractSceneParser(),
+ m_parseDone(false)
+{
+}
+
+GLTFParser::~GLTFParser()
+{
+
+}
+
+void GLTFParser::setBasePath(const QString& path)
+{
+ m_basePath = path;
+}
+
+bool GLTFParser::setJSON(const QJsonDocument &json )
+{
+ if ( !json.isObject() ) {
+ return false;
+ }
+
+ m_json = json;
+ m_parseDone = false;
+
+ cleanup();
+
+ return true;
+}
+
+/*!
+ * Sets the \a path used by the parser to load the scene file.
+ * If the file is valid, parsing is automatically triggered.
+ */
+void GLTFParser::setSource(const QUrl &source)
+{
+ const QString path = QUrlHelper::urlToLocalFileOrQrc(source);
+ QFileInfo finfo(path);
+ if (!finfo.exists()) {
+ qCWarning(GLTFParserLog) << "missing file:" << path;
+ return;
+ }
+ QFile f(path);
+ f.open(QIODevice::ReadOnly);
+
+ if (!setJSON(QJsonDocument::fromJson(f.readAll()))) {
+ qCWarning(GLTFParserLog) << "not a JSON document";
+ return;
+ }
+
+ setBasePath(finfo.dir().absolutePath());
+}
+
+/*!
+ * Returns true if the extension of \a path is supported by the
+ * GLTF parser.
+ */
+bool GLTFParser::isExtensionSupported(const QUrl &source) const
+{
+ const QString path = QUrlHelper::urlToLocalFileOrQrc(source);
+ return GLTFParser::isGLTFPath(path);
+}
+
+QEntity* GLTFParser::node(const QString &id)
+{
+ QJsonObject nodes = m_json.object().value(KEY_NODES).toObject();
+ if (!nodes.contains(id)) {
+ qCWarning(GLTFParserLog) << "unknown node" << id << "in GLTF file" << m_basePath;
+ return NULL;
+ }
+
+ QJsonObject jsonObj = nodes.value(id).toObject();
+ QEntity* result = Q_NULLPTR;
+
+ // Qt3D has a limitation that a QEntity can only have 1 mesh and 1 material component
+ // So if the node has only 1 mesh, we only create 1 QEntity
+ // Otherwise if there are n meshes, there is 1 QEntity, with n children for each mesh/material combo
+ if (jsonObj.contains(KEY_MESHES)) {
+ QVector<QEntity *> entities;
+
+ Q_FOREACH (QJsonValue mesh, jsonObj.value(KEY_MESHES).toArray()) {
+ if (!m_meshDict.contains(mesh.toString())) {
+ qCWarning(GLTFParserLog) << "node" << id << "references unknown mesh" << mesh.toString();
+ continue;
+ }
+
+ Q_FOREACH (QGeometryRenderer *geometryRenderer, m_meshDict.values(mesh.toString())) {
+ QEntity *entity = new QEntity;
+ entity->addComponent(geometryRenderer);
+ entity->addComponent(material(m_meshMaterialDict[geometryRenderer]));
+ entities.append(entity);
+ }
+
+ }
+
+ if (entities.count() == 1) {
+ result = entities.first();
+ } else {
+ result = new QEntity;
+ Q_FOREACH (QEntity *entity, entities) {
+ entity->setParent(result);
+ }
+ }
+ }
+
+ //If the entity contains no meshes, results will still be null here
+ if (result == Q_NULLPTR)
+ result = new QEntity;
+
+ if ( jsonObj.contains(KEY_CHILDREN) ) {
+ Q_FOREACH (QJsonValue c, jsonObj.value(KEY_CHILDREN).toArray()) {
+ QEntity* child = node(c.toString());
+ if (!child)
+ continue;
+ child->setParent(result);
+ }
+ }
+
+ renameFromJson(jsonObj, result);
+
+ if ( jsonObj.contains(KEY_MATRIX) ) {
+ QMatrix4x4 m(Qt::Uninitialized);
+
+ QJsonArray matrixValues = jsonObj.value(KEY_MATRIX).toArray();
+ for (int i=0; i<16; ++i) {
+ double v = matrixValues.at( i ).toDouble();
+ m(i % 4, i >> 2) = v;
+ }
+
+ // ADD MATRIX TRANSFORM COMPONENT TO ENTITY
+ QTransform *trans = new QTransform();
+ trans->addTransform(new QMatrixTransform(m));
+ result->addComponent(trans);
+ }
+
+ if ( jsonObj.contains(KEY_CAMERA) ) {
+ QCameraLens* cam = camera( jsonObj.value(KEY_CAMERA).toString() );
+ if (!cam) {
+ qCWarning(GLTFParserLog) << "failed to build camera:" << jsonObj.value(KEY_CAMERA)
+ << "on node" << id;
+ } else {
+ result->addComponent(cam);
+ }
+ } // of have camera attribute
+
+ return result;
+}
+
+QEntity* GLTFParser::scene(const QString &id)
+{
+ parse();
+
+ QJsonObject scenes = m_json.object().value(KEY_SCENES).toObject();
+ if (!scenes.contains(id)) {
+ if (!id.isNull())
+ qCWarning(GLTFParserLog) << "GLTF: no such scene" << id << "in file" << m_basePath;
+ return defaultScene();
+ }
+
+ QJsonObject sceneObj = scenes.value(id).toObject();
+ QEntity* sceneEntity = new QEntity;
+ Q_FOREACH (QJsonValue nnv, sceneObj.value(KEY_NODES).toArray()) {
+ QString nodeName = nnv.toString();
+ QEntity* child = node(nodeName);
+ if (!child)
+ continue;
+ child->setParent(sceneEntity);
+ }
+
+ return sceneEntity;
+}
+
+GLTFParser::BufferData::BufferData()
+ : length(0)
+ , data(Q_NULLPTR)
+{
+}
+
+GLTFParser::BufferData::BufferData(QJsonObject json)
+{
+ path = json.value(KEY_URI).toString();
+ length = json.value(KEY_BYTE_LENGTH).toInt();
+ data = Q_NULLPTR;
+}
+
+GLTFParser::ParameterData::ParameterData() :
+ type(0)
+{
+
+}
+
+GLTFParser::ParameterData::ParameterData(QJsonObject json)
+{
+ type = json.value(KEY_TYPE).toInt();
+ semantic = json.value(KEY_SEMANTIC).toString();
+}
+
+GLTFParser::AccessorData::AccessorData()
+ : type(QAttribute::Float)
+ , dataSize(0)
+ , count(0)
+ , offset(0)
+ , stride(0)
+{
+
+}
+
+GLTFParser::AccessorData::AccessorData(const QJsonObject &json)
+{
+ bufferViewName = json.value(KEY_BUFFER_VIEW).toString();
+ offset = 0;
+ stride = 0;
+ int componentType = json.value(KEY_COMPONENT_TYPE).toInt();
+ type = accessorTypeFromJSON(componentType);
+ count = json.value(KEY_COUNT).toInt();
+ dataSize = accessorDataSizeFromJson(json.value(KEY_TYPE).toString());
+
+ if ( json.contains(KEY_BYTE_OFFSET))
+ offset = json.value(KEY_BYTE_OFFSET).toInt();
+ if ( json.contains(KEY_BYTE_STRIDE))
+ stride = json.value(KEY_BYTE_STRIDE).toInt();
+}
+
+bool GLTFParser::isGLTFPath(const QString& path)
+{
+ QFileInfo finfo(path);
+ if (!finfo.exists())
+ return false;
+
+ // might need to detect other things in the future, but would
+ // prefer to avoid doing a full parse.
+ QString suffix = finfo.suffix().toLower();
+ return (suffix == QStringLiteral("json") || suffix == QStringLiteral("gltf"));
+}
+
+void GLTFParser::renameFromJson(const QJsonObject &json, QObject * const object)
+{
+ if ( json.contains(KEY_NAME) )
+ object->setObjectName( json.value(KEY_NAME).toString() );
+}
+
+QString GLTFParser::standardUniformNamefromSemantic(const QString &semantic)
+{
+ //Standard Uniforms
+ //if (semantic == QStringLiteral("LOCAL"));
+ if (semantic == QStringLiteral("MODEL"))
+ return QStringLiteral("modelMatrix");
+ if (semantic == QStringLiteral("VIEW"))
+ return QStringLiteral("viewMatrix");
+ if (semantic == QStringLiteral("PROJECTION"))
+ return QStringLiteral("projectionMatrix");
+ if (semantic == QStringLiteral("MODELVIEW"))
+ return QStringLiteral("modelView");
+ if (semantic == QStringLiteral("MODELVIEWPROJECTION"))
+ return QStringLiteral("modelViewProjection");
+ if (semantic == QStringLiteral("MODELINVERSE"))
+ return QStringLiteral("inverseModelMatrix");
+ if (semantic == QStringLiteral("VIEWINVERSE"))
+ return QStringLiteral("inverViewMatrix");
+ if (semantic == QStringLiteral("PROJECTIONINVERSE"))
+ return QStringLiteral("inverseProjectionMatrix");
+ if (semantic == QStringLiteral("MODELVIEWPROJECTIONINVERSE"))
+ return QStringLiteral("inverseModelViewProjection");
+ if (semantic == QStringLiteral("MODELINVERSETRANSPOSE"))
+ return QStringLiteral("modelNormalMatrix");
+ if (semantic == QStringLiteral("MODELVIEWINVERSETRANSPOSE"))
+ return QStringLiteral("modelViewNormal");
+ if (semantic == QStringLiteral("VIEWPORT"))
+ return QStringLiteral("viewportMatrix");
+
+ return QString();
+}
+
+QString GLTFParser::standardAttributeNameFromSemantic(const QString &semantic)
+{
+ //Standard Attributes
+ if (semantic.startsWith(QStringLiteral("POSITION")))
+ return QAttribute::defaultPositionAttributeName();
+ if (semantic.startsWith(QStringLiteral("NORMAL")))
+ return QAttribute::defaultNormalAttributeName();
+ if (semantic.startsWith(QStringLiteral("TEXCOORD")))
+ return QAttribute::defaultTextureCoordinateAttributeName();
+ if (semantic.startsWith(QStringLiteral("COLOR")))
+ return QAttribute::defaultColorAttributeName();
+ if (semantic.startsWith(QStringLiteral("TANGENT")))
+ return QAttribute::defaultTangentAttributeName();
+
+// if (semantic.startsWith(QStringLiteral("JOINT")));
+// if (semantic.startsWith(QStringLiteral("JOINTMATRIX")));
+// if (semantic.startsWith(QStringLiteral("WEIGHT")));
+
+ return QString();
+}
+
+QEntity* GLTFParser::defaultScene()
+{
+ if (m_defaultScene.isEmpty()) {
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "no default scene";
+ return NULL;
+ }
+
+ return scene(m_defaultScene);
+}
+
+QMaterial* GLTFParser::material(const QString &id)
+{
+ if (m_materialCache.contains(id))
+ return m_materialCache.value(id);
+
+ QJsonObject mats = m_json.object().value(KEY_MATERIALS).toObject();
+ if (!mats.contains(id)) {
+ qCWarning(GLTFParserLog) << "unknown material" << id << "in GLTF file" << m_basePath;
+ return NULL;
+ }
+
+ QJsonObject jsonObj = mats.value(id).toObject();
+
+ QJsonObject tech = jsonObj.value(KEY_INSTANCE_TECHNIQUE).toObject();
+ QString tname = tech.value(KEY_TECHNIQUE).toString();
+ if (!m_techniques.contains(tname)) {
+ qCWarning(GLTFParserLog) << "unknown technique" << tname
+ << "for material" << id << "in GLTF file" << m_basePath;
+ return NULL;
+ }
+
+ QTechnique *technique = m_techniques.value(tname);
+ // glTF doesn't deal in effects, but we need a trivial one to wrap
+ // up our techniques
+ // However we need to create a unique effect for each material instead
+ // of caching because QMaterial does not keep up with effects
+ // its not the parent of.
+ QEffect* effect = new QEffect;
+ effect->setObjectName(tname);
+ effect->addTechnique(technique);
+
+ QMaterial* mat = new QMaterial;
+ mat->setEffect(effect);
+
+ renameFromJson(jsonObj, mat);
+
+ QJsonObject values = tech.value(KEY_VALUES).toObject();
+ Q_FOREACH (QString vName, values.keys()) {
+ QParameter *param = Q_NULLPTR;
+ Q_FOREACH (QParameter *parameter, technique->parameters()) {
+ if (parameter->name() == vName) {
+ param = parameter;
+ break;
+ }
+ }
+ if (param == Q_NULLPTR) {
+ qCWarning(GLTFParserLog) << "unknown parameter:" << vName << "in technique" << tname
+ << "processing material" << id;
+ continue;
+ }
+
+ ParameterData paramData = m_parameterDataDict.value(param);
+ QVariant var = parameterValueFromJSON(paramData.type, values.value(vName));
+
+ mat->addParameter(new QParameter(param->name(), var));
+ } // of material technique-instance values iteration
+
+ m_materialCache[id] = mat;
+ return mat;
+}
+
+QCameraLens* GLTFParser::camera(const QString &id) const
+{
+ QJsonObject cams = m_json.object().value(KEY_CAMERAS).toObject();
+ if (!cams.contains(id)) {
+ qCWarning(GLTFParserLog) << "unknown camera" << id << "in GLTF file" << m_basePath;
+ return Q_NULLPTR;
+ }
+
+ QJsonObject jsonObj = cams.value(id).toObject();
+ QString camTy = jsonObj.value(KEY_TYPE).toString();
+
+ if (camTy == QStringLiteral("perspective")) {
+ if (!jsonObj.contains(KEY_PERSPECTIVE)) {
+ qCWarning(GLTFParserLog) << "camera:" << id << "missing 'perspective' object";
+ return Q_NULLPTR;
+ }
+
+ QJsonObject pObj = jsonObj.value(KEY_PERSPECTIVE).toObject();
+ double aspectRatio = pObj.value(KEY_ASPECT_RATIO).toDouble();
+ double yfov = pObj.value(KEY_YFOV).toDouble();
+ double frustumNear = pObj.value(KEY_ZNEAR).toDouble();
+ double frustumFar = pObj.value(KEY_ZFAR).toDouble();
+
+ QCameraLens* result = new QCameraLens;
+ result->setPerspectiveProjection(yfov, aspectRatio, frustumNear, frustumFar);
+ return result;
+ } else if (camTy == QStringLiteral("orthographic")) {
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "implement me";
+
+ return Q_NULLPTR;
+ } else {
+ qCWarning(GLTFParserLog) << "camera:" << id << "has unsupported type:" << camTy;
+ return Q_NULLPTR;
+ }
+}
+
+
+void GLTFParser::parse()
+{
+ if (m_parseDone)
+ return;
+
+ QJsonObject buffers = m_json.object().value(KEY_BUFFERS).toObject();
+ Q_FOREACH (QString nm, buffers.keys()) {
+ processJSONBuffer( nm, buffers.value(nm).toObject() );
+ }
+
+ QJsonObject views = m_json.object().value(KEY_BUFFER_VIEWS).toObject();
+ loadBufferData();
+ Q_FOREACH (QString nm, views.keys()) {
+ processJSONBufferView( nm, views.value(nm).toObject() );
+ }
+ unloadBufferData();
+
+ QJsonObject shaders = m_json.object().value(KEY_SHADERS).toObject();
+ Q_FOREACH (QString nm, shaders.keys()) {
+ processJSONShader( nm, shaders.value(nm).toObject() );
+ }
+
+ QJsonObject programs = m_json.object().value(KEY_PROGRAMS).toObject();
+ Q_FOREACH (QString nm, programs.keys()) {
+ processJSONProgram( nm, programs.value(nm).toObject() );
+ }
+
+ QJsonObject techniques = m_json.object().value(KEY_TECHNIQUES).toObject();
+ Q_FOREACH (QString nm, techniques.keys()) {
+ processJSONTechnique( nm, techniques.value(nm).toObject() );
+ }
+
+ QJsonObject attrs = m_json.object().value(KEY_ACCESSORS).toObject();
+ Q_FOREACH (QString nm, attrs.keys()) {
+ processJSONAccessor( nm, attrs.value(nm).toObject() );
+ }
+
+ QJsonObject meshes = m_json.object().value(KEY_MESHES).toObject();
+ Q_FOREACH (QString nm, meshes.keys()) {
+ processJSONMesh( nm, meshes.value(nm).toObject() );
+ }
+
+ QJsonObject images = m_json.object().value(KEY_IMAGES).toObject();
+ Q_FOREACH (QString nm, images.keys()) {
+ processJSONImage( nm, images.value(nm).toObject() );
+ }
+
+ QJsonObject textures = m_json.object().value(KEY_TEXTURES).toObject();
+ Q_FOREACH (QString nm, textures.keys()) {
+ processJSONTexture(nm, textures.value(nm).toObject() );
+ }
+
+ m_defaultScene = m_json.object().value(KEY_SCENE).toString();
+ m_parseDone = true;
+}
+
+void GLTFParser::cleanup()
+{
+ m_meshDict.clear();
+ m_meshMaterialDict.clear();
+ m_accessorDict.clear();
+ //Check for Materials with no parent
+ Q_FOREACH (QMaterial *material, m_materialCache.values()) {
+ if (material->parent() == Q_NULLPTR)
+ delete material;
+ }
+ m_materialCache.clear();
+ m_bufferDatas.clear();
+ m_buffers.clear();
+ m_shaderPaths.clear();
+ //Check for ShaderPrograms with no parent
+ Q_FOREACH (QShaderProgram *program, m_programs.values()) {
+ if (program->parent() == Q_NULLPTR)
+ delete program;
+ }
+ m_programs.clear();
+ //Check for Techniques with no parent
+ Q_FOREACH (QTechnique *technique, m_techniques.values()) {
+ if (technique->parent() == Q_NULLPTR)
+ delete technique;
+ }
+ m_techniques.clear();
+ //Check for Textures with no parent
+ Q_FOREACH (QAbstractTextureProvider *texture, m_textures.values()) {
+ if (texture->parent() == Q_NULLPTR)
+ delete texture;
+ }
+ m_textures.clear();
+ m_imagePaths.clear();
+ m_defaultScene.clear();
+ m_parameterDataDict.clear();
+}
+
+void GLTFParser::processJSONBuffer(const QString &id, const QJsonObject& json)
+{
+ // simply cache buffers for lookup by buffer-views
+ m_bufferDatas[id] = BufferData(json);
+}
+
+void GLTFParser::processJSONBufferView(const QString &id, const QJsonObject& json)
+{
+ QString bufName = json.value(KEY_BUFFER).toString();
+ if (!m_bufferDatas.contains(bufName)) {
+ qCWarning(GLTFParserLog) << "unknown buffer:" << bufName << "processing view:" << id;
+ return;
+ }
+
+ int target = json.value(KEY_TARGET).toInt();
+ QBuffer::BufferType ty(QBuffer::VertexBuffer);
+
+ switch (target) {
+ case GL_ARRAY_BUFFER: ty = QBuffer::VertexBuffer; break;
+ case GL_ELEMENT_ARRAY_BUFFER: ty = QBuffer::IndexBuffer; break;
+ default:
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "buffer" << id << "unsupported target:" << target;
+ return;
+ }
+
+ quint64 offset = 0;
+ if (json.contains(KEY_BYTE_OFFSET)) {
+ offset = json.value(KEY_BYTE_OFFSET).toInt();
+ qCDebug(GLTFParserLog) << "bv:" << id << "has offset:" << offset;
+ }
+
+ quint64 len = json.value(KEY_BYTE_LENGTH).toInt();
+
+ QByteArray bytes(m_bufferDatas[bufName].data->mid(offset, len));
+ if (bytes.count() != (int) len) {
+ qCWarning(GLTFParserLog) << "failed to read sufficient bytes from:" << m_bufferDatas[bufName].path
+ << "for view" << id;
+ }
+
+ QBuffer *b(new QBuffer(ty));
+ b->setData(bytes);
+ m_buffers[id] = b;
+}
+
+void GLTFParser::processJSONShader(const QString &id, const QJsonObject &jsonObject)
+{
+ // shaders are trivial for the moment, defer the real work
+ // to the program section
+ QString path = jsonObject.value(KEY_URI).toString();
+
+ QFileInfo info(m_basePath, path);
+ if (!info.exists()) {
+ qCWarning(GLTFParserLog) << "can't find shader" << id << "from path" << path;
+ return;
+ }
+
+ m_shaderPaths[id] = info.absoluteFilePath();
+}
+
+void GLTFParser::processJSONProgram(const QString &id, const QJsonObject &jsonObject)
+{
+ QShaderProgram* prog = new QShaderProgram;
+ prog->setObjectName(id);
+
+ QString fragName = jsonObject.value(KEY_FRAGMENT_SHADER).toString(),
+ vertName = jsonObject.value(KEY_VERTEX_SHADER).toString();
+ if (!m_shaderPaths.contains(fragName) || !m_shaderPaths.contains(vertName)) {
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "program:" << id << "missing shader:"
+ << fragName << vertName;
+ return;
+ }
+
+ prog->setFragmentShaderCode(Qt3D::QShaderProgram::loadSource(QUrl::fromLocalFile(m_shaderPaths[fragName])));
+ prog->setVertexShaderCode(Qt3D::QShaderProgram::loadSource(QUrl::fromLocalFile(m_shaderPaths[vertName])));
+ m_programs[id] = prog;
+}
+
+void GLTFParser::processJSONTechnique(const QString &id, const QJsonObject &jsonObject )
+{
+ QTechnique *t = new QTechnique;
+ t->setObjectName(id);
+
+ QHash<QString, QParameter*> paramDict;
+ QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
+ Q_FOREACH (QString pname, params.keys()) {
+ QJsonObject po = params.value(pname).toObject();
+
+ //QString semantic = po.value(KEY_SEMANTIC).toString();
+ QParameter *p = new QParameter(t);
+ p->setName(pname);
+ m_parameterDataDict.insert(p, ParameterData(po));
+
+ //If the parameter has default value, set it
+ QJsonValue value = po.value(KEY_VALUE);
+ if (!value.isUndefined()) {
+ int dataType = po.value(KEY_TYPE).toInt();
+ p->setValue(parameterValueFromJSON(dataType, value));
+ }
+
+ t->addParameter(p);
+
+ paramDict[pname] = p;
+ } // of parameters iteration
+
+ QJsonObject passes = jsonObject.value(KEY_PASSES).toObject();
+ Q_FOREACH (QString pname, passes.keys()) {
+ QJsonObject po = passes.value(pname).toObject();
+ QJsonObject ip = po.value(KEY_INSTANCE_PROGRAM).toObject();
+
+ QString programName = ip.value(KEY_PROGRAM).toString();
+ if (!m_programs.contains(programName)) {
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "technique" << id << "pass" << pname
+ << ": missing program" << programName;
+ continue;
+ }
+
+ QRenderPass* pass = new QRenderPass;
+ pass->setShaderProgram(m_programs[programName]);
+
+ QJsonObject attrs = ip.value(KEY_ATTRIBUTES).toObject();
+ Q_FOREACH ( QString shaderAttributeName, attrs.keys() ) {
+ QString pname = attrs.value(shaderAttributeName).toString();
+ QParameter *parameter = paramDict.value(pname, Q_NULLPTR);
+ QString attributeName = pname;
+ if (parameter == Q_NULLPTR) {
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "attribute " << pname
+ << "defined in instanceProgram but not as parameter";
+ continue;
+ }
+ //Check if the parameter has a standard attribute semantic
+ QString standardAttributeName = standardAttributeNameFromSemantic(m_parameterDataDict[parameter].semantic);
+ if (!standardAttributeName.isNull()) {
+ attributeName = standardAttributeName;
+ t->removeParameter(parameter);
+ m_parameterDataDict.remove(parameter);
+ delete parameter;
+ }
+
+ pass->addBinding(new QParameterMapping(attributeName, shaderAttributeName, QParameterMapping::Attribute));
+ } // of program-instance attributes
+
+ QJsonObject uniforms = ip.value(KEY_UNIFORMS).toObject();
+ Q_FOREACH (QString shaderUniformName, uniforms.keys()) {
+ QString pname = uniforms.value(shaderUniformName).toString();
+ QParameter *parameter = paramDict.value(pname, Q_NULLPTR);
+ if (parameter == Q_NULLPTR) {
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "uniform " << pname
+ << "defined in instanceProgram but not as parameter";
+ continue;
+ }
+ //Check if the parameter has a standard uniform semantic
+ QString standardUniformName = standardUniformNamefromSemantic(m_parameterDataDict[parameter].semantic);
+ if (standardUniformName.isNull()) {
+ pass->addBinding(new QParameterMapping(pname, shaderUniformName, QParameterMapping::Uniform));
+ } else {
+ pass->addBinding(new QParameterMapping(standardUniformName, shaderUniformName, QParameterMapping::StandardUniform));
+ t->removeParameter(parameter);
+ m_parameterDataDict.remove(parameter);
+ delete parameter;
+ }
+ } // of program-instance uniforms
+
+ QJsonObject states = po.value(KEY_STATES).toObject();
+
+ //Process states to enable
+ QJsonArray enableStatesArray = states.value(KEY_ENABLE).toArray();
+ QVector<int> enableStates;
+ Q_FOREACH (QJsonValue enableValue, enableStatesArray) {
+ enableStates.append(enableValue.toInt());
+ }
+
+ //Process the list of state functions
+ QJsonObject functions = states.value(KEY_FUNCTIONS).toObject();
+ Q_FOREACH (QString functionName, functions.keys()) {
+ int enableStateType = 0;
+ QRenderState *renderState = buildState(functionName, functions.value(functionName), enableStateType);
+ if (renderState != Q_NULLPTR) {
+ //Remove the need to set a default state values for enableStateType
+ enableStates.removeOne(enableStateType);
+ pass->addRenderState(renderState);
+ }
+ }
+
+ //Create render states with default values for any remaining enable states
+ Q_FOREACH (int enableState, enableStates) {
+ QRenderState *renderState = buildStateEnable(enableState);
+ if (renderState != Q_NULLPTR)
+ pass->addRenderState(renderState);
+ }
+
+
+ t->addPass(pass);
+ } // of passes iteration
+
+ m_techniques[id] = t;
+}
+
+void GLTFParser::processJSONAccessor( const QString &id, const QJsonObject& json )
+{
+ m_accessorDict[id] = AccessorData(json);
+}
+
+void GLTFParser::processJSONMesh(const QString &id, const QJsonObject &json)
+{
+ QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
+ Q_FOREACH (QJsonValue primitiveValue, primitivesArray) {
+ QJsonObject primitiveObject = primitiveValue.toObject();
+ int type = primitiveObject.value(KEY_PRIMITIVE).toInt();
+ QString material = primitiveObject.value(KEY_MATERIAL).toString();
+
+ if ( material.isEmpty()) {
+ qCWarning(GLTFParserLog) << "malformed primitive on " << id << ", missing material value"
+ << material;
+ continue;
+ }
+
+ QGeometryRenderer *geometryRenderer = new QGeometryRenderer;
+ QGeometry *meshGeometry = new QGeometry(geometryRenderer);
+
+ //Set Primitive Type
+ geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type));
+
+ //Save Material for mesh
+ m_meshMaterialDict[geometryRenderer] = material;
+
+ QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject();
+ Q_FOREACH (QString attrName, attrs.keys()) {
+ QString k = attrs.value(attrName).toString();
+ if (!m_accessorDict.contains(k)) {
+ qCWarning(GLTFParserLog) << "unknown attribute accessor:" << k << "on mesh" << id;
+ continue;
+ }
+
+ QString attributeName = standardAttributeNameFromSemantic(attrName);
+ if (attributeName.isEmpty())
+ attributeName = attrName;
+
+ //Get buffer handle for accessor
+ QBuffer *buffer = m_buffers.value(m_accessorDict[k].bufferViewName, Q_NULLPTR);
+ if (buffer == Q_NULLPTR) {
+ qCWarning(GLTFParserLog) << "unknown buffer-view:" << m_accessorDict[k].bufferViewName << "processing accessor:" << id;
+ continue;
+ }
+
+ QAttribute *attribute = new QAttribute(buffer,
+ attributeName,
+ m_accessorDict[k].type,
+ m_accessorDict[k].dataSize,
+ m_accessorDict[k].count,
+ m_accessorDict[k].offset,
+ m_accessorDict[k].stride);
+ attribute->setAttributeType(QAttribute::VertexAttribute);
+ meshGeometry->addAttribute(attribute);
+ }
+
+ if ( primitiveObject.contains(KEY_INDICES)) {
+ QString k = primitiveObject.value(KEY_INDICES).toString();
+ if (!m_accessorDict.contains(k)) {
+ qCWarning(GLTFParserLog) << "unknown index accessor:" << k << "on mesh" << id;
+ } else {
+ //Get buffer handle for accessor
+ QBuffer *buffer = m_buffers.value(m_accessorDict[k].bufferViewName, Q_NULLPTR);
+ if (buffer == Q_NULLPTR) {
+ qCWarning(GLTFParserLog) << "unknown buffer-view:" << m_accessorDict[k].bufferViewName << "processing accessor:" << id;
+ continue;
+ }
+
+ QAttribute *attribute = new QAttribute(buffer,
+ m_accessorDict[k].type,
+ m_accessorDict[k].dataSize,
+ m_accessorDict[k].count,
+ m_accessorDict[k].offset,
+ m_accessorDict[k].stride);
+ attribute->setAttributeType(QAttribute::IndexAttribute);
+ meshGeometry->addAttribute(attribute);
+ }
+ } // of has indices
+
+ geometryRenderer->setGeometry(meshGeometry);
+
+ m_meshDict.insert( id, geometryRenderer);
+ } // of primitives iteration
+}
+
+void GLTFParser::processJSONImage(const QString &id, const QJsonObject &jsonObject)
+{
+ QString path = jsonObject.value(KEY_URI).toString();
+ QFileInfo info(m_basePath, path);
+ if (!info.exists()) {
+ qCWarning(GLTFParserLog) << "can't find image" << id << "from path" << path;
+ return;
+ }
+
+ m_imagePaths[id] = info.absoluteFilePath();
+}
+
+void GLTFParser::processJSONTexture(const QString &id, const QJsonObject &jsonObject)
+{
+ int target = jsonObject.value(KEY_TARGET).toInt(GL_TEXTURE_2D);
+ //TODO: support other targets that GL_TEXTURE_2D (though the spec doesn't support anything else)
+ if (target != GL_TEXTURE_2D) {
+ qCWarning(GLTFParserLog) << "unsupported texture target: " << target;
+ return;
+ }
+
+ QTexture2D* tex = new QTexture2D;
+
+ // TODO: Choose suitable internal format - may vary on OpenGL context type
+ //int pixelFormat = jsonObj.value(KEY_FORMAT).toInt(GL_RGBA);
+ int internalFormat = jsonObject.value(KEY_INTERNAL_FORMAT).toInt(GL_RGBA);
+
+ tex->setFormat(static_cast<QAbstractTextureProvider::TextureFormat>(internalFormat));
+
+ QString samplerId = jsonObject.value(KEY_SAMPLER).toString();
+ QString source = jsonObject.value(KEY_SOURCE).toString();
+ if (!m_imagePaths.contains(source)) {
+ qCWarning(GLTFParserLog) << "texture" << id << "references missing image" << source;
+ return;
+ }
+
+ QTextureImage *texImage = new QTextureImage(tex);
+ texImage->setSource(QUrl::fromLocalFile(m_imagePaths[source]));
+ tex->addTextureImage(texImage);
+
+ QJsonObject samplersDict(m_json.object().value(KEY_SAMPLERS).toObject());
+ if (!samplersDict.contains(samplerId)) {
+ qCWarning(GLTFParserLog) << "texture" << id << "references unknown sampler" << samplerId;
+ return;
+ }
+
+ QJsonObject sampler = samplersDict.value(samplerId).toObject();
+
+ tex->setWrapMode(QTextureWrapMode(static_cast<QTextureWrapMode::WrapMode>(sampler.value(KEY_WRAP_S).toInt())));
+ tex->setMinificationFilter(static_cast<QAbstractTextureProvider::Filter>(sampler.value(KEY_MIN_FILTER).toInt()));
+ if (tex->minificationFilter() == QAbstractTextureProvider::NearestMipMapLinear ||
+ tex->minificationFilter() == QAbstractTextureProvider::LinearMipMapNearest ||
+ tex->minificationFilter() == QAbstractTextureProvider::NearestMipMapNearest ||
+ tex->minificationFilter() == QAbstractTextureProvider::LinearMipMapLinear) {
+
+ tex->setGenerateMipMaps(true);
+ }
+ tex->setMagnificationFilter(static_cast<QAbstractTextureProvider::Filter>(sampler.value(KEY_MAG_FILTER).toInt()));
+
+ m_textures[id] = tex;
+}
+
+void GLTFParser::loadBufferData()
+{
+ Q_FOREACH (QString bufferName, m_bufferDatas.keys()) {
+ if (m_bufferDatas[bufferName].data == Q_NULLPTR) {
+ QFile* bufferFile = resolveLocalData(m_bufferDatas[bufferName].path);
+ QByteArray *data = new QByteArray(bufferFile->readAll());
+ m_bufferDatas[bufferName].data = data;
+ delete bufferFile;
+ }
+ }
+}
+
+void GLTFParser::unloadBufferData()
+{
+ Q_FOREACH (QString bufferName, m_bufferDatas.keys()) {
+ QByteArray *data = m_bufferDatas[bufferName].data;
+ delete data;
+ }
+}
+
+QFile *GLTFParser::resolveLocalData(QString path) const
+{
+ QDir d(m_basePath);
+ Q_ASSERT(d.exists());
+
+ QString absPath = d.absoluteFilePath(path);
+ QFile* f = new QFile(absPath);
+ f->open(QIODevice::ReadOnly);
+ return f;
+}
+
+QVariant GLTFParser::parameterValueFromJSON(int type, const QJsonValue &value) const
+{
+ if (value.isBool()) {
+ if (type == GL_BOOL)
+ return QVariant(static_cast<GLboolean>(value.toBool()));
+ } else if (value.isString()) {
+ if (type == GL_SAMPLER_2D) {
+ //Textures are special because we need to do a lookup to return the
+ //QAbstractTextureProvider
+ QString textureId = value.toString();
+ if (!m_textures.contains(textureId)) {
+ qCWarning(GLTFParserLog) << "unknown texture" << textureId;
+ return QVariant();
+ } else {
+ return QVariant::fromValue(m_textures.value(textureId));
+ }
+ }
+ } else if (value.isDouble()) {
+ switch (type) {
+ case GL_BYTE:
+ return QVariant(static_cast<GLbyte>(value.toInt()));
+ case GL_UNSIGNED_BYTE:
+ return QVariant(static_cast<GLubyte>(value.toInt()));
+ case GL_SHORT:
+ return QVariant(static_cast<GLshort>(value.toInt()));
+ case GL_UNSIGNED_SHORT:
+ return QVariant(static_cast<GLushort>(value.toInt()));
+ case GL_INT:
+ return QVariant(static_cast<GLint>(value.toInt()));
+ case GL_UNSIGNED_INT:
+ return QVariant(static_cast<GLuint>(value.toInt()));
+ case GL_FLOAT:
+ return QVariant(static_cast<GLfloat>(value.toDouble()));
+ }
+ } else if (value.isArray()) {
+
+ QJsonArray valueArray = value.toArray();
+
+ QVector2D vector2D;
+ QVector3D vector3D;
+ QVector4D vector4D;
+ QVector<float> dataMat2(4, 0.0f);
+ QVector<float> dataMat3(9, 0.0f);
+
+ switch (type) {
+ case GL_BYTE:
+ return QVariant(static_cast<GLbyte>(valueArray.first().toInt()));
+ case GL_UNSIGNED_BYTE:
+ return QVariant(static_cast<GLubyte>(valueArray.first().toInt()));
+ case GL_SHORT:
+ return QVariant(static_cast<GLshort>(valueArray.first().toInt()));
+ case GL_UNSIGNED_SHORT:
+ return QVariant(static_cast<GLushort>(valueArray.first().toInt()));
+ case GL_INT:
+ return QVariant(static_cast<GLint>(valueArray.first().toInt()));
+ case GL_UNSIGNED_INT:
+ return QVariant(static_cast<GLuint>(valueArray.first().toInt()));
+ case GL_FLOAT:
+ return QVariant(static_cast<GLfloat>(valueArray.first().toDouble()));
+ case GL_FLOAT_VEC2:
+ vector2D.setX(static_cast<GLfloat>(valueArray.at(0).toDouble()));
+ vector2D.setY(static_cast<GLfloat>(valueArray.at(1).toDouble()));
+ return QVariant(vector2D);
+ case GL_FLOAT_VEC3:
+ vector3D.setX(static_cast<GLfloat>(valueArray.at(0).toDouble()));
+ vector3D.setY(static_cast<GLfloat>(valueArray.at(1).toDouble()));
+ vector3D.setZ(static_cast<GLfloat>(valueArray.at(2).toDouble()));
+ return QVariant(vector3D);
+ case GL_FLOAT_VEC4:
+ vector4D.setX(static_cast<GLfloat>(valueArray.at(0).toDouble()));
+ vector4D.setY(static_cast<GLfloat>(valueArray.at(1).toDouble()));
+ vector4D.setZ(static_cast<GLfloat>(valueArray.at(2).toDouble()));
+ vector4D.setW(static_cast<GLfloat>(valueArray.at(3).toDouble()));
+ return QVariant(vector4D);
+ case GL_INT_VEC2:
+ vector2D.setX(static_cast<GLint>(valueArray.at(0).toInt()));
+ vector2D.setY(static_cast<GLint>(valueArray.at(1).toInt()));
+ return QVariant(vector2D);
+ case GL_INT_VEC3:
+ vector3D.setX(static_cast<GLint>(valueArray.at(0).toInt()));
+ vector3D.setY(static_cast<GLint>(valueArray.at(1).toInt()));
+ vector3D.setZ(static_cast<GLint>(valueArray.at(2).toInt()));
+ return QVariant(vector3D);
+ case GL_INT_VEC4:
+ vector4D.setX(static_cast<GLint>(valueArray.at(0).toInt()));
+ vector4D.setY(static_cast<GLint>(valueArray.at(1).toInt()));
+ vector4D.setZ(static_cast<GLint>(valueArray.at(2).toInt()));
+ vector4D.setW(static_cast<GLint>(valueArray.at(3).toInt()));
+ return QVariant(vector4D);
+ case GL_BOOL:
+ return QVariant(static_cast<GLboolean>(valueArray.first().toBool()));
+ case GL_BOOL_VEC2:
+ vector2D.setX(static_cast<GLboolean>(valueArray.at(0).toBool()));
+ vector2D.setY(static_cast<GLboolean>(valueArray.at(1).toBool()));
+ return QVariant(vector2D);
+ case GL_BOOL_VEC3:
+ vector3D.setX(static_cast<GLboolean>(valueArray.at(0).toBool()));
+ vector3D.setY(static_cast<GLboolean>(valueArray.at(1).toBool()));
+ vector3D.setZ(static_cast<GLboolean>(valueArray.at(2).toBool()));
+ return QVariant(vector3D);
+ case GL_BOOL_VEC4:
+ vector4D.setX(static_cast<GLboolean>(valueArray.at(0).toBool()));
+ vector4D.setY(static_cast<GLboolean>(valueArray.at(1).toBool()));
+ vector4D.setZ(static_cast<GLboolean>(valueArray.at(2).toBool()));
+ vector4D.setW(static_cast<GLboolean>(valueArray.at(3).toBool()));
+ return QVariant(vector4D);
+ case GL_FLOAT_MAT2:
+ //Matrix2x2 is in Row Major ordering (so we need to convert)
+ dataMat2[0] = static_cast<GLfloat>(valueArray.at(0).toDouble());
+ dataMat2[1] = static_cast<GLfloat>(valueArray.at(2).toDouble());
+ dataMat2[2] = static_cast<GLfloat>(valueArray.at(1).toDouble());
+ dataMat2[3] = static_cast<GLfloat>(valueArray.at(3).toDouble());
+ return QVariant::fromValue(QMatrix2x2(dataMat2.constData()));
+ case GL_FLOAT_MAT3:
+ //Matrix3x3 is in Row Major ordering (so we need to convert)
+ dataMat3[0] = static_cast<GLfloat>(valueArray.at(0).toDouble());
+ dataMat3[1] = static_cast<GLfloat>(valueArray.at(3).toDouble());
+ dataMat3[2] = static_cast<GLfloat>(valueArray.at(6).toDouble());
+ dataMat3[3] = static_cast<GLfloat>(valueArray.at(1).toDouble());
+ dataMat3[4] = static_cast<GLfloat>(valueArray.at(4).toDouble());
+ dataMat3[5] = static_cast<GLfloat>(valueArray.at(7).toDouble());
+ dataMat3[6] = static_cast<GLfloat>(valueArray.at(2).toDouble());
+ dataMat3[7] = static_cast<GLfloat>(valueArray.at(5).toDouble());
+ dataMat3[8] = static_cast<GLfloat>(valueArray.at(8).toDouble());
+ return QVariant::fromValue(QMatrix3x3(dataMat3.constData()));
+ case GL_FLOAT_MAT4:
+ //Matrix4x4 is Column Major ordering
+ return QVariant(QMatrix4x4(static_cast<GLfloat>(valueArray.at(0).toDouble()),
+ static_cast<GLfloat>(valueArray.at(1).toDouble()),
+ static_cast<GLfloat>(valueArray.at(2).toDouble()),
+ static_cast<GLfloat>(valueArray.at(3).toDouble()),
+ static_cast<GLfloat>(valueArray.at(4).toDouble()),
+ static_cast<GLfloat>(valueArray.at(5).toDouble()),
+ static_cast<GLfloat>(valueArray.at(6).toDouble()),
+ static_cast<GLfloat>(valueArray.at(7).toDouble()),
+ static_cast<GLfloat>(valueArray.at(8).toDouble()),
+ static_cast<GLfloat>(valueArray.at(9).toDouble()),
+ static_cast<GLfloat>(valueArray.at(10).toDouble()),
+ static_cast<GLfloat>(valueArray.at(11).toDouble()),
+ static_cast<GLfloat>(valueArray.at(12).toDouble()),
+ static_cast<GLfloat>(valueArray.at(13).toDouble()),
+ static_cast<GLfloat>(valueArray.at(14).toDouble()),
+ static_cast<GLfloat>(valueArray.at(15).toDouble())));
+ case GL_SAMPLER_2D:
+ return QVariant(valueArray.at(0).toString());
+ }
+ }
+ return QVariant();
+}
+
+QAttribute::DataType GLTFParser::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_FLOAT) {
+ return QAttribute::Float;
+ }
+
+ //There shouldn't be an invalid case here
+ qCWarning(GLTFParserLog) << "unsupported accessor type" << componentType;
+ return QAttribute::Float;
+}
+
+uint GLTFParser::accessorDataSizeFromJson(const QString &type)
+{
+ QString typeName = type.toUpper();
+ if (typeName == "SCALAR")
+ return 1;
+ if (typeName == "VEC2")
+ return 2;
+ if (typeName == "VEC3")
+ return 3;
+ if (typeName == "VEC4")
+ return 4;
+ if (typeName == "MAT2")
+ return 4;
+ if (typeName == "MAT3")
+ return 9;
+ if (typeName == "MAT4")
+ return 16;
+
+ return 0;
+}
+
+QRenderState *GLTFParser::buildStateEnable(int state)
+{
+ int type = 0;
+ //By calling buildState with QJsonValue(), a Render State with
+ //default values is created.
+
+ if (state == GL_BLEND) {
+ //It doesn't make sense to handle this state alone
+ return Q_NULLPTR;
+ }
+
+ if (state == GL_CULL_FACE) {
+ return buildState(QStringLiteral("cullFace"), QJsonValue(), type);
+ }
+
+ if (state == GL_DEPTH_TEST) {
+ return buildState(QStringLiteral("depthFunc"), QJsonValue(), type);
+ }
+
+ if (state == GL_POLYGON_OFFSET_FILL) {
+ return buildState(QStringLiteral("polygonOffset"), QJsonValue(), type);
+ }
+
+ if (state == GL_SAMPLE_ALPHA_TO_COVERAGE) {
+ return new QAlphaCoverage();
+ }
+
+ if (state == GL_SCISSOR_TEST) {
+ return buildState(QStringLiteral("scissor"), QJsonValue(), type);
+ }
+
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << state;
+
+ return Q_NULLPTR;
+}
+
+QRenderState* GLTFParser::buildState(const QString& functionName, const QJsonValue &value, int &type)
+{
+ type = -1;
+ QJsonArray values = value.toArray();
+
+ if (functionName == QStringLiteral("blendColor")) {
+ type = GL_BLEND;
+ //TODO: support render state blendColor
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << functionName;
+ return Q_NULLPTR;
+ }
+
+ if (functionName == QStringLiteral("blendEquationSeparate")) {
+ type = GL_BLEND;
+ //TODO: support settings blendEquation alpha
+ QBlendEquation *blendEquation = new QBlendEquation;
+ blendEquation->setMode((QBlendEquation::BlendMode)values.at(0).toInt(GL_FUNC_ADD));
+ return blendEquation;
+ }
+
+ if (functionName == QStringLiteral("blendFuncSeparate")) {
+ type = GL_BLEND;
+ QBlendStateSeparate *blendState = new QBlendStateSeparate;
+ blendState->setSrcRGB((QBlendState::Blending)values.at(0).toInt(GL_ONE));
+ blendState->setSrcAlpha((QBlendState::Blending)values.at(1).toInt(GL_ONE));
+ blendState->setDstRGB((QBlendState::Blending)values.at(2).toInt(GL_ZERO));
+ blendState->setDstAlpha((QBlendState::Blending)values.at(3).toInt(GL_ZERO));
+ return blendState;
+ }
+
+ if (functionName == QStringLiteral("colorMask")) {
+ QColorMask *colorMask = new QColorMask;
+ colorMask->setRed(values.at(0).toBool(true));
+ colorMask->setGreen(values.at(1).toBool(true));
+ colorMask->setBlue(values.at(2).toBool(true));
+ colorMask->setAlpha(values.at(3).toBool(true));
+ return colorMask;
+ }
+
+ if (functionName == QStringLiteral("cullFace")) {
+ type = GL_CULL_FACE;
+ QCullFace *cullFace = new QCullFace;
+ cullFace->setMode((QCullFace::CullingMode)values.at(0).toInt(GL_BACK));
+ return cullFace;
+ }
+
+ if (functionName == QStringLiteral("depthFunc")) {
+ type = GL_DEPTH_TEST;
+ QDepthTest *depthTest = new QDepthTest;
+ depthTest->setFunc((QDepthTest::DepthFunc)values.at(0).toInt(GL_LESS));
+ return depthTest;
+ }
+
+ if (functionName == QStringLiteral("depthMask")) {
+ QDepthMask *depthMask = new QDepthMask;
+ depthMask->setMask(values.at(0).toBool(true));
+ }
+
+ if (functionName == QStringLiteral("depthRange")) {
+ //TODO: support render state depthRange
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << functionName;
+ return Q_NULLPTR;
+ }
+
+ if (functionName == QStringLiteral("frontFace")) {
+ QFrontFace *frontFace = new QFrontFace;
+ frontFace->setDirection((QFrontFace::FaceDir)values.at(0).toInt(GL_CCW));
+ return frontFace;
+ }
+
+ if (functionName == QStringLiteral("lineWidth")) {
+ //TODO: support render state lineWidth
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << functionName;
+ return Q_NULLPTR;
+ }
+
+ if (functionName == QStringLiteral("polygonOffset")) {
+ type = GL_POLYGON_OFFSET_FILL;
+ QPolygonOffset *polygonOffset = new QPolygonOffset;
+ polygonOffset->setFactor((float)values.at(0).toDouble(0.0f));
+ polygonOffset->setUnits((float)values.at(1).toDouble(0.0f));
+ return polygonOffset;
+ }
+
+ if (functionName == QStringLiteral("scissor")) {
+ type = GL_SCISSOR_TEST;
+ QScissorTest *scissorTest = new QScissorTest;
+ scissorTest->setLeft(values.at(0).toDouble(0.0f));
+ scissorTest->setBottom(values.at(1).toDouble(0.0f));
+ scissorTest->setWidth(values.at(2).toDouble(0.0f));
+ scissorTest->setHeight(values.at(3).toDouble(0.0f));
+ return scissorTest;
+ }
+
+ qCWarning(GLTFParserLog) << Q_FUNC_INFO << "unsupported render state:" << functionName;
+ return Q_NULLPTR;
+}
+
+} // namespace Qt3D
+
+QT_END_NAMESPACE
diff --git a/src/plugins/sceneparsers/gltf/gltfparser_p.h b/src/plugins/sceneparsers/gltf/gltfparser_p.h
new file mode 100644
index 000000000..5aed37f53
--- /dev/null
+++ b/src/plugins/sceneparsers/gltf/gltfparser_p.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GLTFPARSER_H
+#define GLTFPARSER_H
+
+#include <QtCore/QJsonDocument>
+#include <QtCore/QMultiHash>
+
+#include <Qt3DRenderer/qattribute.h>
+#include <Qt3DRenderer/qbuffer.h>
+#include <Qt3DRenderer/qmeshdata.h>
+
+#include <Qt3DRenderer/private/abstractsceneparser_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFile;
+
+namespace Qt3D {
+
+class QMaterial;
+class QShaderProgram;
+class QEffect;
+class QCamera;
+class QCameraLens;
+class QAbstractTextureProvider;
+class QRenderState;
+class QTechnique;
+class QParameter;
+class QEntity;
+class QGeometryRenderer;
+
+Q_DECLARE_LOGGING_CATEGORY(GLTFParserLog)
+
+class GLTFParser : public AbstractSceneParser
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt3DRenderer.GLTFParser")
+
+public:
+ GLTFParser();
+ ~GLTFParser();
+
+ void setBasePath(const QString& path);
+ bool setJSON( const QJsonDocument &json );
+
+ // SceneParserInterface interface
+ void setSource(const QUrl &source) Q_DECL_FINAL;
+ bool isExtensionSupported(const QUrl &source) const Q_DECL_FINAL;
+ QEntity *node(const QString &id) Q_DECL_FINAL;
+ QEntity *scene(const QString &id = QString()) Q_DECL_FINAL;
+
+private:
+ class BufferData
+ {
+ public:
+ BufferData();
+
+ BufferData(QJsonObject json);
+
+ quint64 length;
+ QString path;
+ QByteArray *data;
+ // type if ever useful
+ };
+
+ class ParameterData
+ {
+ public:
+ ParameterData();
+ ParameterData(QJsonObject json);
+
+ QString semantic;
+ int type;
+ };
+
+ class AccessorData
+ {
+ public:
+ AccessorData();
+ AccessorData(const QJsonObject& json);
+
+ QString bufferViewName;
+ QAttribute::DataType type;
+ uint dataSize;
+ int count;
+ int offset;
+ int stride;
+ };
+
+ static bool isGLTFPath(const QString &path);
+ static void renameFromJson(const QJsonObject& json, QObject * const object );
+ static QString standardUniformNamefromSemantic(const QString &semantic);
+ static QString standardAttributeNameFromSemantic(const QString &semantic);
+
+ QEntity *defaultScene();
+ QMaterial *material(const QString &id);
+ QCameraLens *camera(const QString &id) const;
+
+ void parse();
+ void cleanup();
+
+ void processJSONBuffer(const QString &id, const QJsonObject &json);
+ void processJSONBufferView(const QString &id, const QJsonObject &json);
+ void processJSONShader(const QString &id, const QJsonObject &jsonObject);
+ void processJSONProgram(const QString &id, const QJsonObject &jsonObject);
+ void processJSONTechnique(const QString &id, const QJsonObject &jsonObject);
+ void processJSONAccessor(const QString &id, const QJsonObject &json);
+ void processJSONMesh(const QString &id, const QJsonObject &json);
+ void processJSONImage(const QString &id, const QJsonObject &jsonObject);
+ void processJSONTexture(const QString &id, const QJsonObject &jsonObject);
+
+ void loadBufferData();
+ void unloadBufferData();
+
+ QFile* resolveLocalData(QString path) const;
+
+ QVariant parameterValueFromJSON(int type, const QJsonValue &value) const;
+ static QAbstractAttribute::DataType accessorTypeFromJSON(int componentType);
+ static uint accessorDataSizeFromJson(const QString &type);
+
+ static QRenderState *buildStateEnable(int state);
+ static QRenderState *buildState(const QString& functionName, const QJsonValue &value, int &type);
+
+ QJsonDocument m_json;
+ QString m_basePath;
+ bool m_parseDone;
+ QString m_defaultScene;
+
+ // multi-hash because our QMeshData corresponds to a single primitive
+ // in glTf.
+ QMultiHash<QString, QGeometryRenderer*> m_meshDict;
+
+ // GLTF assigns materials at the mesh level, but we do them as siblings,
+ // so record the association here for when we instantiate meshes
+ QMap<QGeometryRenderer*, QString> m_meshMaterialDict;
+
+ QMap<QString, AccessorData> m_accessorDict;
+
+ QMap<QString, QMaterial*> m_materialCache;
+
+ QMap<QString, BufferData> m_bufferDatas;
+ QMap<QString, QBuffer*> m_buffers;
+
+ QMap<QString, QString> m_shaderPaths;
+ QMap<QString, QShaderProgram*> m_programs;
+
+ QMap<QString, QTechnique *> m_techniques;
+ QMap<QParameter*, ParameterData> m_parameterDataDict;
+
+ QMap<QString, QAbstractTextureProvider*> m_textures;
+ QMap<QString, QString> m_imagePaths;
+};
+
+} // namespace Qt3D
+
+QT_END_NAMESPACE
+
+#endif // GLTFPARSER_H
diff --git a/src/plugins/sceneparsers/sceneparsers.pro b/src/plugins/sceneparsers/sceneparsers.pro
index 53899e493..f7a922991 100644
--- a/src/plugins/sceneparsers/sceneparsers.pro
+++ b/src/plugins/sceneparsers/sceneparsers.pro
@@ -1,2 +1,4 @@
TEMPLATE = subdirs
config_assimp|win32:!wince*|osx: SUBDIRS += assimp
+SUBDIRS += gltf
+