summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMassimo Callegari <massimocallegari@yahoo.it>2018-12-21 11:02:13 +0100
committerMassimo Callegari <massimocallegari@yahoo.it>2018-12-22 07:47:53 +0000
commitcc4c126d29e43c51e16a1c3fdcc0c7987e84ee42 (patch)
tree1f82f0107e2cd90c45ccb55aa1c0cdf4c783796a
parent02ac30f7d12df759146f3a5ce000a5f1cb7998fe (diff)
Preliminary glTF 2.0 scene import support
- add parsing of assets and detect the format version - add support for embedded resources - add composite metalness+roughness texture support See notes in cpp file. Task-number: QTBUG-61258 Change-Id: I40f2922d3e2391eb384527a6808fe1509a4aff96 Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
-rw-r--r--src/plugins/sceneparsers/gltf/gltfimporter.cpp772
-rw-r--r--src/plugins/sceneparsers/gltf/gltfimporter.h15
2 files changed, 635 insertions, 152 deletions
diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.cpp b/src/plugins/sceneparsers/gltf/gltfimporter.cpp
index 996077b16..79f2bf2d3 100644
--- a/src/plugins/sceneparsers/gltf/gltfimporter.cpp
+++ b/src/plugins/sceneparsers/gltf/gltfimporter.cpp
@@ -84,6 +84,7 @@
#include <Qt3DRender/qshaderprogram.h>
#include <Qt3DRender/qtechnique.h>
#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qtextureimagedatagenerator.h>
#include <Qt3DRender/qdirectionallight.h>
#include <Qt3DRender/qspotlight.h>
#include <Qt3DRender/qpointlight.h>
@@ -97,6 +98,7 @@
#include <Qt3DExtras/qnormaldiffusespecularmapmaterial.h>
#include <Qt3DExtras/qgoochmaterial.h>
#include <Qt3DExtras/qpervertexcolormaterial.h>
+#include <Qt3DExtras/qmetalroughmaterial.h>
#include <Qt3DExtras/qconemesh.h>
#include <Qt3DExtras/qcuboidmesh.h>
#include <Qt3DExtras/qcylindermesh.h>
@@ -106,17 +108,60 @@
#include <private/qurlhelper_p.h>
+/**
+ * glTF 2.0 conformance report
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
+ * Samples: https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0
+ *
+ * Most of the reference samples are rendered correctly, with the following exceptions:
+ *
+ * 'extensions' and 'extras' are ignored everywhere except in nodes.
+ *
+ * asset
+ * generator, copyright, minVersion: not parsed
+ * accessors
+ * min, max, normalized, sparse: not parsed
+ * animations
+ * the whole object is not parsed
+ * buffers
+ * all parsed
+ * bufferViews
+ * all parsed
+ * cameras
+ * all parsed
+ * images
+ * mimeType, bufferView, name: not parsed
+ * materials
+ * emissiveTexture, emissiveFactor: not parsed
+ * alphaMode, alphaCutoff, doubleSided: not parsed
+ * texCoord, strength: not parsed
+ * meshes
+ * weights: not parsed
+ * nodes
+ * skin, weights: not parsed
+ * samplers
+ * all parsed
+ * scenes
+ * all parsed
+ * textures
+ * all parsed
+ */
+
#ifndef qUtf16PrintableImpl // -Impl is a Qt 5.8 feature
# define qUtf16PrintableImpl(string) \
static_cast<const wchar_t*>(static_cast<const void*>(string.utf16()))
#endif
+#define KEY_ASSET QLatin1String("asset")
+#define KEY_VERSION QLatin1String("version")
#define KEY_CAMERA QLatin1String("camera")
#define KEY_CAMERAS QLatin1String("cameras")
#define KEY_SCENES QLatin1String("scenes")
#define KEY_NODES QLatin1String("nodes")
#define KEY_MESHES QLatin1String("meshes")
#define KEY_CHILDREN QLatin1String("children")
+#define KEY_MESH QLatin1String("mesh")
#define KEY_MATRIX QLatin1String("matrix")
#define KEY_ROTATION QLatin1String("rotation")
#define KEY_SCALE QLatin1String("scale")
@@ -181,6 +226,15 @@
#define KEY_LINEAR_ATTENUATION QLatin1String("linearAttenuation")
#define KEY_QUAD_ATTENUATION QLatin1String("quadraticAttenuation")
#define KEY_INTENSITY QLatin1String("intensity")
+#define KEY_PBR_METAL_ROUGH QLatin1String("pbrMetallicRoughness")
+#define KEY_BASE_COLOR QLatin1String("baseColorFactor")
+#define KEY_BASE_COLOR_TEX QLatin1String("baseColorTexture")
+#define KEY_METAL_FACTOR QLatin1String("metallicFactor")
+#define KEY_METAL_ROUGH_TEX QLatin1String("metallicRoughnessTexture")
+#define KEY_ROUGH_FACTOR QLatin1String("roughnessFactor")
+#define KEY_NORMAL_TEX QLatin1String("normalTexture")
+#define KEY_OCCLUSION_TEX QLatin1String("occlusionTexture")
+#define KEY_INDEX QLatin1String("index")
#define KEY_INSTANCE_TECHNIQUE QLatin1String("instanceTechnique")
#define KEY_INSTANCE_PROGRAM QLatin1String("instanceProgram")
@@ -229,6 +283,18 @@ inline QVector3D jsonArrToVec3(const QJsonArray &array)
return QVector3D(array[0].toDouble(), array[1].toDouble(), array[2].toDouble());
}
+inline QVector4D jsonArrToVec4(const QJsonArray &array)
+{
+ return QVector4D(array[0].toDouble(), array[1].toDouble(),
+ array[2].toDouble(), array[3].toDouble());
+}
+
+inline QVariant jsonArrToColorVariant(const QJsonArray &array)
+{
+ return QVariant(QColor::fromRgbF(array[0].toDouble(), array[1].toDouble(),
+ array[2].toDouble(), array[3].toDouble()));
+}
+
inline QColor vec4ToQColor(const QVariant &vec4Var)
{
const QVector4D v = vec4Var.value<QVector4D>();
@@ -255,11 +321,40 @@ Qt3DRender::QFilterKey *buildFilterKey(const QString &key, const QJsonValue &val
namespace Qt3DRender {
-Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport", QtWarningMsg)
+Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport", QtWarningMsg);
+class GLTFRawTextureImage : public QAbstractTextureImage
+{
+ Q_OBJECT
+public:
+ explicit GLTFRawTextureImage(QNode *parent = nullptr);
+
+ QTextureImageDataGeneratorPtr dataGenerator() const final;
+
+ void setImage(const QImage &image);
+
+private:
+ QImage m_image;
-GLTFImporter::GLTFImporter() : QSceneImporter(),
- m_parseDone(false)
+ class GLTFRawTextureImageFunctor : public QTextureImageDataGenerator
+ {
+ public:
+ explicit GLTFRawTextureImageFunctor(const QImage &image);
+
+ QTextureImageDataPtr operator()() final;
+ bool operator ==(const QTextureImageDataGenerator &other) const final;
+
+ QT3D_FUNCTOR(GLTFRawTextureImageFunctor)
+ private:
+ QImage m_image;
+ };
+};
+
+GLTFImporter::GLTFImporter()
+ : QSceneImporter()
+ , m_parseDone(false)
+ , m_majorVersion(1)
+ , m_minorVersion(0)
{
}
@@ -286,9 +381,9 @@ void GLTFImporter::setBasePath(const QString& path)
Set a \a json document as the file used for importing a scene.
Returns true if the operation is successful.
*/
-bool GLTFImporter::setJSON(const QJsonDocument &json )
+bool GLTFImporter::setJSON(const QJsonDocument &json)
{
- if ( !json.isObject() ) {
+ if (!json.isObject()) {
return false;
}
@@ -360,12 +455,24 @@ bool GLTFImporter::areFileTypesSupported(const QStringList &extensions) const
*/
Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
{
- QJsonObject nodes = m_json.object().value(KEY_NODES).toObject();
- const auto jsonVal = nodes.value(id);
- if (Q_UNLIKELY(jsonVal.isUndefined())) {
- qCWarning(GLTFImporterLog, "unknown node %ls in GLTF file %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return NULL;
+ QJsonValue jsonVal;
+
+ if (m_majorVersion > 1) {
+ const QJsonArray nodes = m_json.object().value(KEY_NODES).toArray();
+ if (Q_UNLIKELY(id.toInt() >= nodes.count())) {
+ qCWarning(GLTFImporterLog, "unknown node %ls in GLTF file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return nullptr;
+ }
+ jsonVal = nodes[id.toInt()];
+ } else {
+ const QJsonObject nodes = m_json.object().value(KEY_NODES).toObject();
+ jsonVal = nodes.value(id);
+ if (Q_UNLIKELY(jsonVal.isUndefined())) {
+ qCWarning(GLTFImporterLog, "unknown node %ls in GLTF file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return nullptr;
+ }
}
const QJsonObject jsonObj = jsonVal.toObject();
@@ -376,27 +483,44 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
// Otherwise if there are n meshes, there is 1 QEntity, with n children for each mesh/material combo
{
QVector<QEntity *> entities;
-
- const auto meshes = jsonObj.value(KEY_MESHES).toArray();
- for (const QJsonValue &mesh : meshes) {
- const QString meshName = mesh.toString();
- const auto geometryRenderers = qAsConst(m_meshDict).equal_range(meshName);
- if (Q_UNLIKELY(geometryRenderers.first == geometryRenderers.second)) {
- qCWarning(GLTFImporterLog, "node %ls references unknown mesh %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(meshName));
- continue;
+ const QJsonValue meshesValue = jsonObj.value(KEY_MESHES);
+
+ if (meshesValue.isUndefined()) {
+ const QJsonValue mesh = jsonObj.value(KEY_MESH);
+ if (!mesh.isUndefined()) {
+ const QString meshName = QString::number(mesh.toInt());
+ const auto geometryRenderers = qAsConst(m_meshDict).equal_range(meshName);
+ for (auto it = geometryRenderers.first; it != geometryRenderers.second; ++it) {
+ QGeometryRenderer *geometryRenderer = it.value();
+ QEntity *entity = new QEntity;
+ entity->addComponent(geometryRenderer);
+ QMaterial *mat = material(m_meshMaterialDict[geometryRenderer]);
+ if (mat)
+ entity->addComponent(mat);
+ entities.append(entity);
+ }
}
+ } else {
+ const auto meshes = meshesValue.toArray();
+ for (const QJsonValue &mesh : meshes) {
+ const QString meshName = mesh.toString();
+ const auto geometryRenderers = qAsConst(m_meshDict).equal_range(meshName);
+ if (Q_UNLIKELY(geometryRenderers.first == geometryRenderers.second)) {
+ qCWarning(GLTFImporterLog, "node %ls references unknown mesh %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(meshName));
+ continue;
+ }
- for (auto it = geometryRenderers.first; it != geometryRenderers.second; ++it) {
- QGeometryRenderer *geometryRenderer = it.value();
- QEntity *entity = new QEntity;
- entity->addComponent(geometryRenderer);
- QMaterial *mat = material(m_meshMaterialDict[geometryRenderer]);
- if (mat)
- entity->addComponent(mat);
- entities.append(entity);
+ for (auto it = geometryRenderers.first; it != geometryRenderers.second; ++it) {
+ QGeometryRenderer *geometryRenderer = it.value();
+ QEntity *entity = new QEntity;
+ entity->addComponent(geometryRenderer);
+ QMaterial *mat = material(m_meshMaterialDict[geometryRenderer]);
+ if (mat)
+ entity->addComponent(mat);
+ entities.append(entity);
+ }
}
-
}
switch (entities.size()) {
@@ -437,14 +561,13 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
}
}
- {
- const auto children = jsonObj.value(KEY_CHILDREN).toArray();
- for (const QJsonValue &c : children) {
- QEntity* child = node(c.toString());
- if (!child)
- continue;
- child->setParent(result);
- }
+ // recursively retrieve children
+ const auto children = jsonObj.value(KEY_CHILDREN).toArray();
+ for (const QJsonValue &c : children) {
+ QEntity* child = node((m_majorVersion > 1) ? QString::number(c.toInt()) : c.toString());
+ if (!child)
+ continue;
+ child->setParent(result);
}
renameFromJson(jsonObj, result);
@@ -454,8 +577,8 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
QMatrix4x4 m(Qt::Uninitialized);
QJsonArray matrixValues = matrix.toArray();
- for (int i=0; i<16; ++i) {
- double v = matrixValues.at( i ).toDouble();
+ for (int i = 0; i < 16; ++i) {
+ double v = matrixValues.at(i).toDouble();
m(i % 4, i >> 2) = v;
}
@@ -469,11 +592,7 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
if (!trans)
trans = new Qt3DCore::QTransform;
- const QJsonArray quaternionValues = rotation.toArray();
- QQuaternion quaternion(quaternionValues[0].toDouble(),
- quaternionValues[1].toDouble(),
- quaternionValues[2].toDouble(),
- quaternionValues[3].toDouble());
+ QQuaternion quaternion(jsonArrToVec4(rotation.toArray()));
trans->setRotation(quaternion);
}
@@ -499,9 +618,10 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
const bool newLens = cameraLens == nullptr;
if (newLens)
cameraLens = new QCameraLens;
- if (!fillCamera(*cameraLens, cameraEntity, cameraVal.toString())) {
+ const QString cameraID = (m_majorVersion > 1) ? QString::number(cameraVal.toInt()) : cameraVal.toString();
+ if (!fillCamera(*cameraLens, cameraEntity, cameraID)) {
qCWarning(GLTFImporterLog, "failed to build camera: %ls on node %ls",
- qUtf16PrintableImpl(cameraVal.toString()), qUtf16PrintableImpl(id));
+ qUtf16PrintableImpl(cameraID), qUtf16PrintableImpl(id));
} else if (newLens) {
result->addComponent(cameraLens);
}
@@ -511,7 +631,8 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
if (!extensionsVal.isUndefined()) {
const auto commonMat = extensionsVal.toObject().value(KEY_COMMON_MAT);
if (!commonMat.isUndefined()) {
- const auto lightId = commonMat.toObject().value(KEY_LIGHT).toString();
+ const QJsonValue lightVal = commonMat.toObject().value(KEY_LIGHT);
+ const QString lightId = (m_majorVersion > 1) ? QString::number(lightVal.toInt()) : lightVal.toString();
QAbstractLight *lightComp = m_lights.value(lightId);
if (Q_UNLIKELY(!lightComp)) {
qCWarning(GLTFImporterLog, "failed to find light: %ls for node %ls",
@@ -532,24 +653,46 @@ Qt3DCore::QEntity* GLTFImporter::scene(const QString &id)
{
parse();
- QJsonObject scenes = m_json.object().value(KEY_SCENES).toObject();
- const auto sceneVal = scenes.value(id);
- if (Q_UNLIKELY(sceneVal.isUndefined())) {
- if (Q_UNLIKELY(!id.isNull()))
- qCWarning(GLTFImporterLog, "GLTF: no such scene %ls in file %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return defaultScene();
- }
+ QEntity* sceneEntity = nullptr;
- QJsonObject sceneObj = sceneVal.toObject();
- QEntity* sceneEntity = new QEntity;
- const auto nodes = sceneObj.value(KEY_NODES).toArray();
- for (const QJsonValue &nnv : nodes) {
- QString nodeName = nnv.toString();
- QEntity* child = node(nodeName);
- if (!child)
- continue;
- child->setParent(sceneEntity);
+ if (m_majorVersion > 1) {
+ const QJsonArray scenes = m_json.object().value(KEY_SCENES).toArray();
+ const auto sceneVal = scenes.first();
+ if (Q_UNLIKELY(sceneVal.isUndefined())) {
+ if (Q_UNLIKELY(!id.isNull()))
+ qCWarning(GLTFImporterLog, "GLTF: no such scene %ls in file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return defaultScene();
+ }
+ const QJsonObject sceneObj = sceneVal.toObject();
+ sceneEntity = new QEntity;
+ const auto nodes = sceneObj.value(KEY_NODES).toArray();
+ for (const QJsonValue &n : nodes) {
+ QEntity* child = node(QString::number(n.toInt()));
+ if (!child)
+ continue;
+ child->setParent(sceneEntity);
+ }
+ } else {
+ const QJsonObject scenes = m_json.object().value(KEY_SCENES).toObject();
+ const auto sceneVal = scenes.value(id);
+ if (Q_UNLIKELY(sceneVal.isUndefined())) {
+ if (Q_UNLIKELY(!id.isNull()))
+ qCWarning(GLTFImporterLog, "GLTF: no such scene %ls in file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return defaultScene();
+ }
+
+ const QJsonObject sceneObj = sceneVal.toObject();
+ sceneEntity = new QEntity;
+ const auto nodes = sceneObj.value(KEY_NODES).toArray();
+ for (const QJsonValue &nnv : nodes) {
+ QString nodeName = nnv.toString();
+ QEntity* child = node(nodeName);
+ if (!child)
+ continue;
+ child->setParent(sceneEntity);
+ }
}
cleanup();
@@ -592,14 +735,21 @@ GLTFImporter::AccessorData::AccessorData()
}
-GLTFImporter::AccessorData::AccessorData(const QJsonObject &json)
- : bufferViewName(json.value(KEY_BUFFER_VIEW).toString()),
- type(accessorTypeFromJSON(json.value(KEY_COMPONENT_TYPE).toInt())),
+GLTFImporter::AccessorData::AccessorData(const QJsonObject &json, int major, int minor)
+ : 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)
{
+ Q_UNUSED(minor)
+
+ if (major > 1) {
+ bufferViewName = QString::number(json.value(KEY_BUFFER_VIEW).toInt());
+ } else {
+ bufferViewName = json.value(KEY_BUFFER_VIEW).toString();
+ }
+
const auto byteOffset = json.value(KEY_BYTE_OFFSET);
if (!byteOffset.isUndefined())
offset = byteOffset.toInt();
@@ -618,6 +768,11 @@ bool GLTFImporter::isGLTFSupported(const QStringList &extensions)
return false;
}
+bool GLTFImporter::isEmbeddedResource(const QString &url)
+{
+ return url.startsWith("data:");
+}
+
void GLTFImporter::renameFromJson(const QJsonObject &json, QObject * const object)
{
const auto name = json.value(KEY_NAME);
@@ -690,7 +845,7 @@ Qt3DCore::QEntity* GLTFImporter::defaultScene()
{
if (Q_UNLIKELY(m_defaultScene.isEmpty())) {
qCWarning(GLTFImporterLog, "no default scene");
- return NULL;
+ return nullptr;
}
return scene(m_defaultScene);
@@ -708,7 +863,7 @@ QMaterial *GLTFImporter::materialWithCustomShader(const QString &id, const QJson
if (Q_UNLIKELY(it == m_techniques.cend())) {
qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
qUtf16PrintableImpl(techniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return NULL;
+ return nullptr;
}
QTechnique *technique = *it;
technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGLES);
@@ -814,7 +969,7 @@ QMaterial *GLTFImporter::commonMaterial(const QJsonObject &jsonObj)
{
const auto jsonExt =
jsonObj.value(KEY_EXTENSIONS).toObject().value(KEY_COMMON_MAT).toObject();
- if (jsonExt.isEmpty())
+ if (m_majorVersion == 1 && jsonExt.isEmpty())
return nullptr;
QVariantHash params;
@@ -823,6 +978,13 @@ QMaterial *GLTFImporter::commonMaterial(const QJsonObject &jsonObj)
bool hasNormalMap = false;
bool hasAlpha = false;
+ if (m_majorVersion > 1) {
+ QMaterial *mat = pbrMaterial(jsonObj);
+
+ if (mat)
+ return mat;
+ }
+
const QJsonObject values = jsonExt.value(KEY_VALUES).toObject();
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
const QString vName = it.key();
@@ -938,18 +1100,146 @@ QMaterial *GLTFImporter::commonMaterial(const QJsonObject &jsonObj)
return mat;
}
+QMaterial *GLTFImporter::pbrMaterial(const QJsonObject &jsonObj)
+{
+ // check for pbrMetallicRoughness material
+ QMetalRoughMaterial *mrMaterial = nullptr;
+ QJsonValue jsonValue = jsonObj.value(KEY_PBR_METAL_ROUGH);
+
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject pbrObj = jsonValue.toObject();
+ mrMaterial = new QMetalRoughMaterial;
+ jsonValue = pbrObj.value(KEY_BASE_COLOR);
+ if (!jsonValue.isUndefined())
+ mrMaterial->setBaseColor(jsonArrToColorVariant(jsonValue.toArray()));
+
+ jsonValue = pbrObj.value(KEY_BASE_COLOR_TEX);
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject texObj = jsonValue.toObject();
+ const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
+ const auto it = m_textures.find(textureId);
+ if (Q_UNLIKELY(it == m_textures.end())) {
+ qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
+ } else {
+ mrMaterial->setBaseColor(QVariant::fromValue(it.value()));
+ }
+ }
+
+ jsonValue = pbrObj.value(KEY_METAL_FACTOR);
+ if (!jsonValue.isUndefined())
+ mrMaterial->setMetalness(jsonValue.toVariant());
+
+ jsonValue = pbrObj.value(KEY_METAL_ROUGH_TEX);
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject texObj = jsonValue.toObject();
+ const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
+ const auto it = m_textures.find(textureId);
+ if (Q_UNLIKELY(it == m_textures.end())) {
+ qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
+ } else {
+ // find the texture again
+ const QJsonArray texArray = m_json.object().value(KEY_TEXTURES).toArray();
+ const QJsonObject tObj = texArray.at(texObj.value(KEY_INDEX).toInt()).toObject();
+ const QString sourceId = QString::number(tObj.value(KEY_SOURCE).toInt());
+ QImage image;
+ if (m_imagePaths.contains(sourceId)) {
+ image.load(m_imagePaths.value(sourceId));
+ } else if (m_imageData.contains(sourceId)) {
+ image = m_imageData.value(sourceId);
+ } else {
+ return mrMaterial;
+ }
+
+ // at this point, in image there is data for metalness (on B) and
+ // roughness (on G) bytes. 2 new textures are created
+ // to make Qt3D happy, since it samples only on R.
+
+ QTexture2D* metalTex = new QTexture2D;
+ QTexture2D* roughTex = new QTexture2D;
+ GLTFRawTextureImage* metalImgTex = new GLTFRawTextureImage();
+ GLTFRawTextureImage* roughImgTex = new GLTFRawTextureImage();
+ QImage metalness(image.size(), image.format());
+ QImage roughness(image.size(), image.format());
+
+ const uchar *imgData = image.constBits();
+ const int pixelBytes = image.depth() / 8;
+ Q_ASSERT_X(pixelBytes < 3, "GLTFImporter::pbrMaterial", "Unsupported texture format");
+
+ for (int y = 0; y < image.height(); y++) {
+ for (int x = 0; x < image.width(); x++) {
+ metalness.setPixel(x, y, qRgb(imgData[0], imgData[0], imgData[0]));
+ roughness.setPixel(x, y, qRgb(imgData[1], imgData[1], imgData[1]));
+ imgData += pixelBytes;
+ }
+ }
+
+ metalImgTex->setImage(metalness);
+ metalTex->addTextureImage(metalImgTex);
+ roughImgTex->setImage(roughness);
+ roughTex->addTextureImage(roughImgTex);
+
+ setTextureSamplerInfo("", tObj, metalTex);
+ setTextureSamplerInfo("", tObj, roughTex);
+
+ mrMaterial->setMetalness(QVariant::fromValue(metalTex));
+ mrMaterial->setRoughness(QVariant::fromValue(roughTex));
+ }
+ }
+
+ jsonValue = pbrObj.value(KEY_ROUGH_FACTOR);
+ if (!jsonValue.isUndefined())
+ mrMaterial->setRoughness(jsonValue.toVariant());
+ }
+
+ jsonValue = jsonObj.value(KEY_NORMAL_TEX);
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject texObj = jsonValue.toObject();
+ const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
+ const auto it = m_textures.find(textureId);
+ if (Q_UNLIKELY(it == m_textures.end())) {
+ qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
+ } else {
+ if (mrMaterial)
+ mrMaterial->setNormal(QVariant::fromValue(it.value()));
+ }
+ }
+
+ jsonValue = jsonObj.value(KEY_OCCLUSION_TEX);
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject texObj = jsonValue.toObject();
+ const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
+ const auto it = m_textures.find(textureId);
+ if (Q_UNLIKELY(it == m_textures.end())) {
+ qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
+ } else {
+ if (mrMaterial)
+ mrMaterial->setAmbientOcclusion(QVariant::fromValue(it.value()));
+ }
+ }
+
+ return mrMaterial;
+}
+
QMaterial* GLTFImporter::material(const QString &id)
{
const auto it = qAsConst(m_materialCache).find(id);
if (it != m_materialCache.cend())
return it.value();
- QJsonObject mats = m_json.object().value(KEY_MATERIALS).toObject();
- const auto jsonVal = mats.value(id);
+ QJsonValue jsonVal;
+
+ if (m_majorVersion > 1) {
+ const QJsonArray mats = m_json.object().value(KEY_MATERIALS).toArray();
+ jsonVal = mats.at(id.toInt());
+ } else {
+ const QJsonObject mats = m_json.object().value(KEY_MATERIALS).toObject();
+ jsonVal = mats.value(id);
+ }
+
if (Q_UNLIKELY(jsonVal.isUndefined())) {
qCWarning(GLTFImporterLog, "unknown material %ls in GLTF file %ls",
qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return NULL;
+ return nullptr;
}
const QJsonObject jsonObj = jsonVal.toObject();
@@ -967,14 +1257,26 @@ QMaterial* GLTFImporter::material(const QString &id)
bool GLTFImporter::fillCamera(QCameraLens &lens, QCamera *cameraEntity, const QString &id) const
{
- const auto jsonVal = m_json.object().value(KEY_CAMERAS).toObject().value(id);
- if (Q_UNLIKELY(jsonVal.isUndefined())) {
- qCWarning(GLTFImporterLog, "unknown camera %ls in GLTF file %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return false;
+ QJsonObject jsonObj;
+
+ if (m_majorVersion > 1) {
+ const QJsonArray camArray = m_json.object().value(KEY_CAMERAS).toArray();
+ if (id.toInt() >= camArray.count()) {
+ qCWarning(GLTFImporterLog, "unknown camera %ls in GLTF file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return false;
+ }
+ jsonObj = camArray[id.toInt()].toObject();
+ } else {
+ const auto jsonVal = m_json.object().value(KEY_CAMERAS).toObject().value(id);
+ if (Q_UNLIKELY(jsonVal.isUndefined())) {
+ qCWarning(GLTFImporterLog, "unknown camera %ls in GLTF file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return false;
+ }
+ jsonObj = jsonVal.toObject();
}
- QJsonObject jsonObj = jsonVal.toObject();
QString camTy = jsonObj.value(KEY_TYPE).toString();
if (camTy == QLatin1String("perspective")) {
@@ -1025,12 +1327,26 @@ bool GLTFImporter::fillCamera(QCameraLens &lens, QCamera *cameraEntity, const QS
return true;
}
-
void GLTFImporter::parse()
{
if (m_parseDone)
return;
+ const QJsonValue asset = m_json.object().value(KEY_ASSET);
+ if (!asset.isUndefined())
+ processJSONAsset(asset.toObject());
+
+ if (m_majorVersion > 1) {
+ parseV2();
+ } else {
+ parseV1();
+ }
+
+ m_parseDone = true;
+}
+
+void GLTFImporter::parseV1()
+{
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());
@@ -1082,7 +1398,38 @@ void GLTFImporter::parse()
processJSONEffect(it.key(), it.value().toObject());
m_defaultScene = m_json.object().value(KEY_SCENE).toString();
- m_parseDone = true;
+}
+
+void GLTFImporter::parseV2()
+{
+ int i;
+ const QJsonArray buffers = m_json.object().value(KEY_BUFFERS).toArray();
+ for (i = 0; i < buffers.count(); i++)
+ processJSONBuffer(QString::number(i), buffers[i].toObject());
+
+ const QJsonArray views = m_json.object().value(KEY_BUFFER_VIEWS).toArray();
+ loadBufferData();
+ for (i = 0; i < views.count(); i++)
+ processJSONBufferView(QString::number(i), views[i].toObject());
+ unloadBufferData();
+
+ const QJsonArray accessors = m_json.object().value(KEY_ACCESSORS).toArray();
+ for (i = 0; i < accessors.count(); i++)
+ processJSONAccessor(QString::number(i), accessors[i].toObject());
+
+ const QJsonArray meshes = m_json.object().value(KEY_MESHES).toArray();
+ for (i = 0; i < meshes.count(); i++)
+ processJSONMesh(QString::number(i), meshes[i].toObject());
+
+ const QJsonArray images = m_json.object().value(KEY_IMAGES).toArray();
+ for (i = 0; i < images.count(); i++)
+ processJSONImage(QString::number(i), images[i].toObject());
+
+ const QJsonArray textures = m_json.object().value(KEY_TEXTURES).toArray();
+ for (i = 0; i < textures.count(); i++)
+ processJSONTexture(QString::number(i), textures[i].toObject());
+
+ m_defaultScene = QString::number(m_json.object().value(KEY_SCENE).toInt());
}
namespace {
@@ -1116,6 +1463,7 @@ void GLTFImporter::cleanup()
delete_if_without_parent(m_textures);
m_textures.clear();
m_imagePaths.clear();
+ m_imageData.clear();
m_defaultScene.clear();
m_parameterDataDict.clear();
delete_if_without_parent(m_renderPasses);
@@ -1124,6 +1472,18 @@ void GLTFImporter::cleanup()
m_effects.clear();
}
+void GLTFImporter::processJSONAsset(const QJsonObject &json)
+{
+ const QString version = json.value(KEY_VERSION).toString();
+ if (!version.isEmpty()) {
+ const QStringList verTokens = version.split('.');
+ if (verTokens.length() >= 2) {
+ m_majorVersion = verTokens[0].toInt();
+ m_minorVersion = verTokens[1].toInt();
+ }
+ }
+}
+
void GLTFImporter::processJSONBuffer(const QString &id, const QJsonObject& json)
{
// simply cache buffers for lookup by buffer-views
@@ -1132,7 +1492,12 @@ void GLTFImporter::processJSONBuffer(const QString &id, const QJsonObject& json)
void GLTFImporter::processJSONBufferView(const QString &id, const QJsonObject& json)
{
- QString bufName = json.value(KEY_BUFFER).toString();
+ QString bufName;
+ if (m_majorVersion > 1) {
+ bufName = QString::number(json.value(KEY_BUFFER).toInt());
+ } else {
+ bufName = json.value(KEY_BUFFER).toString();
+ }
const auto it = qAsConst(m_bufferDatas).find(bufName);
if (Q_UNLIKELY(it == m_bufferDatas.cend())) {
qCWarning(GLTFImporterLog, "unknown buffer: %ls processing view: %ls",
@@ -1141,7 +1506,13 @@ void GLTFImporter::processJSONBufferView(const QString &id, const QJsonObject& j
}
const auto &bufferData = *it;
- int target = json.value(KEY_TARGET).toInt();
+ const QJsonValue targetValue = json.value(KEY_TARGET);
+ int target;
+ if (targetValue.isUndefined()) {
+ target = GL_ARRAY_BUFFER;
+ } else {
+ target = targetValue.toInt();
+ }
Qt3DRender::QBuffer::BufferType ty(Qt3DRender::QBuffer::VertexBuffer);
switch (target) {
@@ -1180,14 +1551,21 @@ void GLTFImporter::processJSONShader(const QString &id, const QJsonObject &jsonO
// to the program section
QString path = jsonObject.value(KEY_URI).toString();
- QFileInfo info(m_basePath, path);
- if (Q_UNLIKELY(!info.exists())) {
- qCWarning(GLTFImporterLog, "can't find shader %ls from path %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
- return;
+ if (!isEmbeddedResource(path)) {
+ QFileInfo info(m_basePath, path);
+ if (Q_UNLIKELY(!info.exists())) {
+ qCWarning(GLTFImporterLog, "can't find shader %ls from path %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
+ return;
+ }
+
+ m_shaderPaths[id] = info.absoluteFilePath();
+ } else {
+ const QByteArray base64Data = path.toLatin1().remove(0, path.indexOf(",") + 1);
+ m_shaderPaths[id] = QString(QByteArray::fromBase64(base64Data));
}
- m_shaderPaths[id] = info.absoluteFilePath();
+
}
void GLTFImporter::processJSONProgram(const QString &id, const QJsonObject &jsonObject)
@@ -1361,9 +1739,9 @@ void GLTFImporter::processJSONTechnique(const QString &id, const QJsonObject &js
m_techniques[id] = t;
}
-void GLTFImporter::processJSONAccessor( const QString &id, const QJsonObject& json )
+void GLTFImporter::processJSONAccessor(const QString &id, const QJsonObject& json)
{
- m_accessorDict[id] = AccessorData(json);
+ m_accessorDict[id] = AccessorData(json, m_majorVersion, m_minorVersion);
}
void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
@@ -1374,22 +1752,27 @@ void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
// Custom mesh
const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
for (const QJsonValue &primitiveValue : primitivesArray) {
- QJsonObject primitiveObject = primitiveValue.toObject();
- int type = primitiveObject.value(KEY_MODE).toInt();
- QString material = primitiveObject.value(KEY_MATERIAL).toString();
+ const QJsonObject primitiveObject = primitiveValue.toObject();
+ const QJsonValue type = primitiveObject.value(KEY_MODE);
+ const QJsonValue matValue = primitiveObject.value(KEY_MATERIAL);
+ const QString material = (m_majorVersion > 1) ? QString::number(matValue.toInt()) : matValue.toString();
QGeometryRenderer *geometryRenderer = new QGeometryRenderer;
QGeometry *meshGeometry = new QGeometry(geometryRenderer);
//Set Primitive Type
- geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type));
+ if (type.isUndefined()) {
+ geometryRenderer->setPrimitiveType(QGeometryRenderer::Triangles);
+ } else {
+ geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type.toInt()));
+ }
//Save Material for mesh
m_meshMaterialDict[geometryRenderer] = material;
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 QString k = (m_majorVersion > 1) ? QString::number(it.value().toInt()) : it.value().toString();
const auto accessorIt = qAsConst(m_accessorDict).find(k);
if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) {
qCWarning(GLTFImporterLog, "unknown attribute accessor: %ls on mesh %ls",
@@ -1424,18 +1807,16 @@ void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
const auto indices = primitiveObject.value(KEY_INDICES);
if (!indices.isUndefined()) {
- QString k = indices.toString();
- const auto accessorIt = qAsConst(m_accessorDict).find(k);
+ const QString accIndex = (m_majorVersion > 1) ? QString::number(indices.toInt()) : indices.toString();
+ const auto accessorIt = qAsConst(m_accessorDict).find(accIndex);
if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) {
qCWarning(GLTFImporterLog, "unknown index accessor: %ls on mesh %ls",
- qUtf16PrintableImpl(k), qUtf16PrintableImpl(id));
+ qUtf16PrintableImpl(accIndex), qUtf16PrintableImpl(id));
} else {
//Get buffer handle for accessor
- Qt3DRender::QBuffer *buffer = m_buffers.value(accessorIt->bufferViewName,
- nullptr);
+ Qt3DRender::QBuffer *buffer = m_buffers.value(accessorIt->bufferViewName, nullptr);
if (Q_UNLIKELY(!buffer)) {
- qCWarning(GLTFImporterLog,
- "unknown buffer-view: %ls processing accessor: %ls",
+ qCWarning(GLTFImporterLog, "unknown buffer-view: %ls processing accessor: %ls",
qUtf16PrintableImpl(accessorIt->bufferViewName),
qUtf16PrintableImpl(id));
continue;
@@ -1505,7 +1886,9 @@ void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
}
}
mesh->setObjectName(meshName);
- m_meshMaterialDict[mesh] = json.value(KEY_MATERIAL).toString();
+ m_meshMaterialDict[mesh] = (m_majorVersion > 1) ?
+ QString::number(json.value(KEY_MATERIAL).toInt()) :
+ json.value(KEY_MATERIAL).toString();
m_meshDict.insert(id, mesh);
}
}
@@ -1514,66 +1897,73 @@ void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
void GLTFImporter::processJSONImage(const QString &id, const QJsonObject &jsonObject)
{
QString path = jsonObject.value(KEY_URI).toString();
- QFileInfo info(m_basePath, path);
- if (Q_UNLIKELY(!info.exists())) {
- qCWarning(GLTFImporterLog, "can't find image %ls from path %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
- return;
- }
- m_imagePaths[id] = info.absoluteFilePath();
+ if (!isEmbeddedResource(path)) {
+ QFileInfo info(m_basePath, path);
+ if (Q_UNLIKELY(!info.exists())) {
+ qCWarning(GLTFImporterLog, "can't find image %ls from path %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
+ return;
+ }
+
+ m_imagePaths[id] = info.absoluteFilePath();
+ } else {
+ const QByteArray base64Data = path.toLatin1().remove(0, path.indexOf(",") + 1);
+ QImage image;
+ image.loadFromData(QByteArray::fromBase64(base64Data));
+ m_imageData[id] = image;
+ }
}
void GLTFImporter::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 (Q_UNLIKELY(target != GL_TEXTURE_2D)) {
- qCWarning(GLTFImporterLog, "unsupported texture target: %d", target);
- return;
+ QJsonValue jsonVal = jsonObject.value(KEY_TARGET);
+ if (!jsonVal.isUndefined()) {
+ int target = jsonVal.toInt(GL_TEXTURE_2D);
+ //TODO: support other targets that GL_TEXTURE_2D (though the spec doesn't support anything else)
+ if (Q_UNLIKELY(target != GL_TEXTURE_2D)) {
+ qCWarning(GLTFImporterLog, "unsupported texture target: %d", 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);
+ int internalFormat = GL_RGBA;
+ jsonVal = jsonObject.value(KEY_INTERNAL_FORMAT);
+ if (!jsonVal.isUndefined())
+ internalFormat = jsonObject.value(KEY_INTERNAL_FORMAT).toInt(GL_RGBA);
tex->setFormat(static_cast<QAbstractTexture::TextureFormat>(internalFormat));
- QString samplerId = jsonObject.value(KEY_SAMPLER).toString();
- QString source = jsonObject.value(KEY_SOURCE).toString();
+ QJsonValue srcValue = jsonObject.value(KEY_SOURCE);
+ QString source = (m_majorVersion > 1) ? QString::number(srcValue.toInt()) : srcValue.toString();
+
const auto imagIt = qAsConst(m_imagePaths).find(source);
if (Q_UNLIKELY(imagIt == m_imagePaths.cend())) {
- qCWarning(GLTFImporterLog, "texture %ls references missing image %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(source));
- return;
- }
-
- QTextureImage *texImage = new QTextureImage(tex);
- texImage->setMirrored(false);
- texImage->setSource(QUrl::fromLocalFile(imagIt.value()));
- tex->addTextureImage(texImage);
+ // if an image is not found in paths, it probably means
+ // it was an embedded resource, referenced in m_imageData
+ const auto embImgIt = qAsConst(m_imageData).find(source);
+ if (Q_UNLIKELY(embImgIt == m_imageData.cend())) {
+ qCWarning(GLTFImporterLog, "texture %ls references missing image %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(source));
+ return;
+ }
- const auto samplersDictValue = m_json.object().value(KEY_SAMPLERS).toObject().value(samplerId);
- if (Q_UNLIKELY(samplersDictValue.isUndefined())) {
- qCWarning(GLTFImporterLog, "texture %ls references unknown sampler %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(samplerId));
- return;
+ QImage img = embImgIt.value();
+ GLTFRawTextureImage *imageData = new GLTFRawTextureImage();
+ imageData->setImage(img);
+ tex->addTextureImage(imageData);
+ } else {
+ QTextureImage *texImage = new QTextureImage(tex);
+ texImage->setMirrored(false);
+ texImage->setSource(QUrl::fromLocalFile(imagIt.value()));
+ tex->addTextureImage(texImage);
}
- QJsonObject sampler = samplersDictValue.toObject();
-
- tex->setWrapMode(QTextureWrapMode(static_cast<QTextureWrapMode::WrapMode>(sampler.value(KEY_WRAP_S).toInt())));
- tex->setMinificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MIN_FILTER).toInt()));
- if (tex->minificationFilter() == QAbstractTexture::NearestMipMapLinear ||
- tex->minificationFilter() == QAbstractTexture::LinearMipMapNearest ||
- tex->minificationFilter() == QAbstractTexture::NearestMipMapNearest ||
- tex->minificationFilter() == QAbstractTexture::LinearMipMapLinear) {
-
- tex->setGenerateMipMaps(true);
- }
- tex->setMagnificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MAG_FILTER).toInt()));
+ setTextureSamplerInfo(id, jsonObject, tex);
m_textures[id] = tex;
}
@@ -1708,10 +2098,16 @@ QByteArray GLTFImporter::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();
+ // check for embedded data
+ if (isEmbeddedResource(path)) {
+ const QByteArray base64Data = path.toLatin1().remove(0, path.indexOf(",") + 1);
+ return QByteArray::fromBase64(base64Data);
+ } else {
+ const QString absPath = d.absoluteFilePath(path);
+ QFile f(absPath);
+ f.open(QIODevice::ReadOnly);
+ return f.readAll();
+ }
}
QVariant GLTFImporter::parameterValueFromJSON(int type, const QJsonValue &value) const
@@ -2167,9 +2563,85 @@ void GLTFImporter::addProgramToPass(QRenderPass *pass, const QString &progName)
pass->setShaderProgram(progIt.value());
}
+void GLTFImporter::setTextureSamplerInfo(const QString &id, const QJsonObject &jsonObj, QTexture2D *tex)
+{
+ QJsonObject sampler;
+ const QJsonValue jsonValue = jsonObj.value(KEY_SAMPLER);
+ if (jsonValue.isUndefined())
+ return;
+
+ if (m_majorVersion > 1) {
+ const int samplerId = jsonValue.toInt();
+ const QJsonArray sArray = m_json.object().value(KEY_SAMPLERS).toArray();
+ if (Q_UNLIKELY(samplerId >= sArray.count())) {
+ qCWarning(GLTFImporterLog, "texture %ls references unknown sampler %d",
+ qUtf16PrintableImpl(id), samplerId);
+ return;
+ }
+ sampler = sArray[samplerId].toObject();
+ } else {
+ const QString samplerId = jsonValue.toString();
+ const QJsonValue samplersDictValue = m_json.object().value(KEY_SAMPLERS).toObject().value(samplerId);
+ if (Q_UNLIKELY(samplersDictValue.isUndefined())) {
+ qCWarning(GLTFImporterLog, "texture %ls references unknown sampler %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(samplerId));
+ return;
+ }
+ sampler = samplersDictValue.toObject();
+ }
+
+ tex->setWrapMode(QTextureWrapMode(static_cast<QTextureWrapMode::WrapMode>(sampler.value(KEY_WRAP_S).toInt())));
+ tex->setMinificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MIN_FILTER).toInt()));
+ if (tex->minificationFilter() == QAbstractTexture::NearestMipMapLinear ||
+ tex->minificationFilter() == QAbstractTexture::LinearMipMapNearest ||
+ tex->minificationFilter() == QAbstractTexture::NearestMipMapNearest ||
+ tex->minificationFilter() == QAbstractTexture::LinearMipMapLinear) {
+
+ tex->setGenerateMipMaps(true);
+ }
+ tex->setMagnificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MAG_FILTER).toInt()));
+}
+
+GLTFRawTextureImage::GLTFRawTextureImage(QNode *parent)
+ : QAbstractTextureImage(parent)
+{
+}
+
+QTextureImageDataGeneratorPtr GLTFRawTextureImage::dataGenerator() const
+{
+ return QTextureImageDataGeneratorPtr(new GLTFRawTextureImageFunctor(m_image));
+}
+
+void GLTFRawTextureImage::setImage(const QImage &image)
+{
+ if (image != m_image) {
+ m_image = image;
+ notifyDataGeneratorChanged();
+ }
+}
+
+GLTFRawTextureImage::GLTFRawTextureImageFunctor::GLTFRawTextureImageFunctor(const QImage &image)
+ : QTextureImageDataGenerator()
+ , m_image(image)
+{
+}
+
+QTextureImageDataPtr GLTFRawTextureImage::GLTFRawTextureImageFunctor::operator()()
+{
+ QTextureImageDataPtr dataPtr = QTextureImageDataPtr::create();
+ // Note: we assume 4 components per pixel and not compressed for now
+ dataPtr->setImage(m_image);
+ return dataPtr;
+}
+
+bool GLTFRawTextureImage::GLTFRawTextureImageFunctor::operator ==(const QTextureImageDataGenerator &other) const
+{
+ const GLTFRawTextureImageFunctor *otherFunctor = functor_cast<GLTFRawTextureImageFunctor>(&other);
+ return (otherFunctor != nullptr && otherFunctor->m_image == m_image);
+}
} // namespace Qt3DRender
QT_END_NAMESPACE
-#include "moc_gltfimporter.cpp"
+#include "gltfimporter.moc"
diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.h b/src/plugins/sceneparsers/gltf/gltfimporter.h
index d47a6a729..271ce5979 100644
--- a/src/plugins/sceneparsers/gltf/gltfimporter.h
+++ b/src/plugins/sceneparsers/gltf/gltfimporter.h
@@ -57,6 +57,7 @@
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qhash.h>
+#include <QtCore/qloggingcategory.h>
#include <Qt3DRender/private/qsceneimporter_p.h>
@@ -82,6 +83,7 @@ class QParameter;
class QGeometryRenderer;
class QAbstractLight;
class QRenderPass;
+class QTexture2D;
Q_DECLARE_LOGGING_CATEGORY(GLTFImporterLog)
@@ -94,7 +96,7 @@ public:
~GLTFImporter();
void setBasePath(const QString& path);
- bool setJSON( const QJsonDocument &json );
+ bool setJSON(const QJsonDocument &json);
// SceneParserInterface interface
void setSource(const QUrl &source) final;
@@ -130,7 +132,7 @@ private:
{
public:
AccessorData();
- explicit AccessorData(const QJsonObject& json);
+ explicit AccessorData(const QJsonObject& json, int major, int minor);
QString bufferViewName;
QAttribute::VertexBaseType type;
@@ -141,6 +143,7 @@ private:
};
static bool isGLTFSupported(const QStringList &extensions);
+ static bool isEmbeddedResource(const QString &url);
static void renameFromJson(const QJsonObject& json, QObject * const object );
static bool hasStandardUniformNameFromSemantic(const QString &semantic);
static QString standardAttributeNameFromSemantic(const QString &semantic);
@@ -151,8 +154,11 @@ private:
bool fillCamera(QCameraLens &lens, QCamera *cameraEntity, const QString &id) const;
void parse();
+ void parseV1();
+ void parseV2();
void cleanup();
+ void processJSONAsset(const QJsonObject &json);
void processJSONBuffer(const QString &id, const QJsonObject &json);
void processJSONBufferView(const QString &id, const QJsonObject &json);
void processJSONShader(const QString &id, const QJsonObject &jsonObject);
@@ -181,12 +187,16 @@ private:
void populateRenderStates(QRenderPass *pass, const QJsonObject &states);
void addProgramToPass(QRenderPass *pass, const QString &progName);
+ void setTextureSamplerInfo(const QString &id, const QJsonObject &jsonObj, QTexture2D *tex);
QMaterial *materialWithCustomShader(const QString &id, const QJsonObject &jsonObj);
QMaterial *commonMaterial(const QJsonObject &jsonObj);
+ QMaterial *pbrMaterial(const QJsonObject &jsonObj);
QJsonDocument m_json;
QString m_basePath;
bool m_parseDone;
+ int m_majorVersion;
+ int m_minorVersion;
QString m_defaultScene;
// multi-hash because our QMeshData corresponds to a single primitive
@@ -215,6 +225,7 @@ private:
QHash<QString, QAbstractTexture*> m_textures;
QHash<QString, QString> m_imagePaths;
+ QHash<QString, QImage> m_imageData;
QHash<QString, QAbstractLight *> m_lights;
};