summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2016-11-15 09:57:32 +0200
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2016-12-09 14:14:28 +0000
commit4f2011cef1565bb9c9f2c0e92d63bb67fb3f661d (patch)
tree41b8220d83ac0fae130e3994403780125435db90
parent79e308da8dd303070e989280f940a8bd6a274fcc (diff)
GLTF exporting materials with custom shaders
Generic materials can now be exported with GLTF exporter and imported back with GLTF importer. Change-Id: I05f1f4d4be414d9a17c7ba91ee139e801dbccae8 Reviewed-by: Antti Määttä <antti.maatta@qt.io>
-rw-r--r--src/plugins/sceneparsers/gltf/gltfimporter.cpp842
-rw-r--r--src/plugins/sceneparsers/gltf/gltfimporter.h18
-rw-r--r--src/plugins/sceneparsers/gltfexport/gltfexporter.cpp1365
-rw-r--r--src/plugins/sceneparsers/gltfexport/gltfexporter.h48
-rw-r--r--tests/auto/render/gltfplugins/images.qrc4
-rw-r--r--tests/auto/render/gltfplugins/ontopmaterial.frag14
-rw-r--r--tests/auto/render/gltfplugins/ontopmaterial.vert20
-rw-r--r--tests/auto/render/gltfplugins/ontopmaterialES2.frag10
-rw-r--r--tests/auto/render/gltfplugins/ontopmaterialES2.vert18
-rw-r--r--tests/auto/render/gltfplugins/tst_gltfplugins.cpp410
10 files changed, 1924 insertions, 825 deletions
diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.cpp b/src/plugins/sceneparsers/gltf/gltfimporter.cpp
index 62a309a2f..61c940eb1 100644
--- a/src/plugins/sceneparsers/gltf/gltfimporter.cpp
+++ b/src/plugins/sceneparsers/gltf/gltfimporter.cpp
@@ -40,89 +40,71 @@
#include "gltfimporter.h"
-#include <QtCore/QDir>
-#include <QtCore/QFileInfo>
-#include <QtCore/QJsonArray>
-#include <QtCore/QJsonObject>
-#include <QtCore/QtMath>
-
-#include <QtGui/QVector2D>
-
-#include <Qt3DRender/QCameraLens>
-#include <Qt3DRender/QCamera>
-#include <Qt3DCore/QEntity>
-#include <Qt3DCore/QTransform>
-
-#include <Qt3DRender/private/qurlhelper_p.h>
-
-#include <Qt3DRender/QAlphaCoverage>
-#include <Qt3DRender/QBlendEquation>
-#include <Qt3DRender/QBlendEquationArguments>
-#include <Qt3DRender/QColorMask>
-#include <Qt3DRender/QCullFace>
-#include <Qt3DRender/QNoDepthMask>
-#include <Qt3DRender/QDepthTest>
-#include <Qt3DRender/QEffect>
-#include <Qt3DRender/QFrontFace>
-#include <Qt3DRender/QGeometry>
-#include <Qt3DRender/QGeometryRenderer>
-#include <Qt3DRender/QMaterial>
-#include <Qt3DRender/QGraphicsApiFilter>
-#include <Qt3DRender/QParameter>
-#include <Qt3DRender/QPolygonOffset>
-#include <Qt3DRender/QRenderState>
-#include <Qt3DRender/QScissorTest>
-#include <Qt3DRender/QShaderProgram>
-#include <Qt3DRender/QTechnique>
-#include <Qt3DRender/QTexture>
-#include <Qt3DRender/QDirectionalLight>
-#include <Qt3DRender/QSpotLight>
-#include <Qt3DRender/QPointLight>
-
-#include <Qt3DExtras/QPhongMaterial>
-#include <Qt3DExtras/QPhongAlphaMaterial>
-#include <Qt3DExtras/QDiffuseMapMaterial>
-#include <Qt3DExtras/QDiffuseSpecularMapMaterial>
-#include <Qt3DExtras/QNormalDiffuseMapMaterial>
-#include <Qt3DExtras/QNormalDiffuseMapAlphaMaterial>
-#include <Qt3DExtras/QNormalDiffuseSpecularMapMaterial>
-#include <Qt3DExtras/QGoochMaterial>
-#include <Qt3DExtras/QPerVertexColorMaterial>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qmath.h>
+
+#include <QtGui/qvector2d.h>
+
+#include <Qt3DCore/qentity.h>
+#include <Qt3DCore/qtransform.h>
+
+#include <Qt3DRender/qcameralens.h>
+#include <Qt3DRender/qcamera.h>
+#include <Qt3DRender/qalphacoverage.h>
+#include <Qt3DRender/qalphatest.h>
+#include <Qt3DRender/qblendequation.h>
+#include <Qt3DRender/qblendequationarguments.h>
+#include <Qt3DRender/qclipplane.h>
+#include <Qt3DRender/qcolormask.h>
+#include <Qt3DRender/qcullface.h>
+#include <Qt3DRender/qdithering.h>
+#include <Qt3DRender/qmultisampleantialiasing.h>
+#include <Qt3DRender/qpointsize.h>
+#include <Qt3DRender/qnodepthmask.h>
+#include <Qt3DRender/qdepthtest.h>
+#include <Qt3DRender/qseamlesscubemap.h>
+#include <Qt3DRender/qstencilmask.h>
+#include <Qt3DRender/qstenciloperation.h>
+#include <Qt3DRender/qstenciloperationarguments.h>
+#include <Qt3DRender/qstenciltest.h>
+#include <Qt3DRender/qstenciltestarguments.h>
+#include <Qt3DRender/qeffect.h>
+#include <Qt3DRender/qfrontface.h>
+#include <Qt3DRender/qgeometry.h>
+#include <Qt3DRender/qgeometryrenderer.h>
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qpolygonoffset.h>
+#include <Qt3DRender/qrenderstate.h>
+#include <Qt3DRender/qscissortest.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qdirectionallight.h>
+#include <Qt3DRender/qspotlight.h>
+#include <Qt3DRender/qpointlight.h>
+
+#include <Qt3DExtras/qphongmaterial.h>
+#include <Qt3DExtras/qphongalphamaterial.h>
+#include <Qt3DExtras/qdiffusemapmaterial.h>
+#include <Qt3DExtras/qdiffusespecularmapmaterial.h>
+#include <Qt3DExtras/qnormaldiffusemapmaterial.h>
+#include <Qt3DExtras/qnormaldiffusemapalphamaterial.h>
+#include <Qt3DExtras/qnormaldiffusespecularmapmaterial.h>
+#include <Qt3DExtras/qgoochmaterial.h>
+#include <Qt3DExtras/qpervertexcolormaterial.h>
+
+#include <private/qurlhelper_p.h>
#ifndef qUtf16PrintableImpl // -Impl is a Qt 5.8 feature
# define qUtf16PrintableImpl(string) \
static_cast<const wchar_t*>(static_cast<const void*>(string.utf16()))
#endif
-QT_BEGIN_NAMESPACE
-
-using namespace Qt3DCore;
-using namespace Qt3DExtras;
-
-namespace {
-
-inline QVector3D jsonArrToVec3(const QJsonArray &array)
-{
- return QVector3D(array[0].toDouble(), array[1].toDouble(), array[2].toDouble());
-}
-
-inline QColor vec4ToQColor(const QVariant &vec4Var)
-{
- const QVector4D v = vec4Var.value<QVector4D>();
- return QColor::fromRgbF(v.x(), v.y(), v.z());
-}
-
-inline QVariant vec4ToColorVariant(const QVariant &vec4Var)
-{
- return QVariant(vec4ToQColor(vec4Var));
-}
-
-} // namespace
-
-namespace Qt3DRender {
-
-Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport")
-
#define KEY_CAMERA QLatin1String("camera")
#define KEY_CAMERAS QLatin1String("cameras")
#define KEY_SCENES QLatin1String("scenes")
@@ -186,7 +168,6 @@ Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport")
#define KEY_DIRECTIONAL_LIGHT QLatin1String("directional")
#define KEY_SPOT_LIGHT QLatin1String("spot")
#define KEY_AMBIENT_LIGHT QLatin1String("ambient")
-#define KEY_TYPE QLatin1String("type")
#define KEY_COLOR QLatin1String("color")
#define KEY_FALLOFF_ANGLE QLatin1String("falloffAngle")
#define KEY_DIRECTION QLatin1String("direction")
@@ -201,6 +182,10 @@ Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport")
#define KEY_BUFFER_VIEW QLatin1String("bufferView")
#define KEY_VERTEX_SHADER QLatin1String("vertexShader")
#define KEY_FRAGMENT_SHADER QLatin1String("fragmentShader")
+#define KEY_TESS_CTRL_SHADER QLatin1String("tessCtrlShader")
+#define KEY_TESS_EVAL_SHADER QLatin1String("tessEvalShader")
+#define KEY_GEOMETRY_SHADER QLatin1String("geometryShader")
+#define KEY_COMPUTE_SHADER QLatin1String("computeShader")
#define KEY_INTERNAL_FORMAT QLatin1String("internalFormat")
#define KEY_COMPONENT_TYPE QLatin1String("componentType")
#define KEY_ASPECT_RATIO QLatin1String("aspect_ratio")
@@ -211,6 +196,57 @@ Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport")
#define KEY_BLEND_FUNCTION QLatin1String("blendFuncSeparate")
#define KEY_TECHNIQUE_CORE QLatin1String("techniqueCore")
#define KEY_TECHNIQUE_GL2 QLatin1String("techniqueGL2")
+#define KEY_GABIFILTER QLatin1String("gapifilter")
+#define KEY_API QLatin1String("api")
+#define KEY_MAJORVERSION QLatin1String("majorVersion")
+#define KEY_MINORVERSION QLatin1String("minorVersion")
+#define KEY_PROFILE QLatin1String("profile")
+#define KEY_VENDOR QLatin1String("vendor")
+#define KEY_FILTERKEYS QLatin1String("filterkeys")
+#define KEY_RENDERPASSES QLatin1String("renderpasses")
+#define KEY_EFFECT QLatin1String("effect")
+#define KEY_EFFECTS QLatin1String("effects")
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+using namespace Qt3DExtras;
+
+namespace {
+
+inline QVector3D jsonArrToVec3(const QJsonArray &array)
+{
+ return QVector3D(array[0].toDouble(), array[1].toDouble(), array[2].toDouble());
+}
+
+inline QColor vec4ToQColor(const QVariant &vec4Var)
+{
+ const QVector4D v = vec4Var.value<QVector4D>();
+ return QColor::fromRgbF(v.x(), v.y(), v.z());
+}
+
+inline QVariant vec4ToColorVariant(const QVariant &vec4Var)
+{
+ return QVariant(vec4ToQColor(vec4Var));
+}
+
+Qt3DRender::QFilterKey *buildFilterKey(const QString &key, const QJsonValue &val)
+{
+ Qt3DRender::QFilterKey *fk = new Qt3DRender::QFilterKey;
+ fk->setName(key);
+ if (val.isString())
+ fk->setValue(val.toString());
+ else
+ fk->setValue(val.toInt());
+ return fk;
+}
+
+} // namespace
+
+namespace Qt3DRender {
+
+Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport")
+
GLTFImporter::GLTFImporter() : QSceneImporter(),
m_parseDone(false)
@@ -591,13 +627,13 @@ QString GLTFImporter::standardAttributeNameFromSemantic(const QString &semantic)
return QString();
}
-QParameter *GLTFImporter::parameterFromTechnique(QTechnique *technique, const QString &parameterName)
+QParameter *GLTFImporter::parameterFromTechnique(QTechnique *technique,
+ const QString &parameterName)
{
- const auto parameters = technique->parameters();
+ const QList<QParameter *> parameters = m_techniqueParameters.value(technique);
for (QParameter *parameter : parameters) {
- if (parameter->name() == parameterName) {
+ if (parameter->name() == parameterName)
return parameter;
- }
}
return nullptr;
@@ -615,99 +651,116 @@ Qt3DCore::QEntity* GLTFImporter::defaultScene()
QMaterial *GLTFImporter::materialWithCustomShader(const QString &id, const QJsonObject &jsonObj)
{
- //Default ES2 Technique
- QString techniqueName = jsonObj.value(KEY_TECHNIQUE).toString();
- const auto it = qAsConst(m_techniques).find(techniqueName);
- 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;
- }
- QTechnique *technique = *it;
- technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGLES);
- technique->graphicsApiFilter()->setMajorVersion(2);
- technique->graphicsApiFilter()->setMinorVersion(0);
- technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
-
+ QString effectName = jsonObj.value(KEY_EFFECT).toString();
+ if (effectName.isEmpty()) {
+ // GLTF custom shader material (with qgltf tool specific customizations)
- //Optional Core technique
- QTechnique *coreTechnique = nullptr;
- QTechnique *gl2Technique = nullptr;
- QString coreTechniqueName = jsonObj.value(KEY_TECHNIQUE_CORE).toString();
- if (!coreTechniqueName.isNull()) {
- const auto it = qAsConst(m_techniques).find(coreTechniqueName);
+ // Default ES2 Technique
+ QString techniqueName = jsonObj.value(KEY_TECHNIQUE).toString();
+ const auto it = qAsConst(m_techniques).find(techniqueName);
if (Q_UNLIKELY(it == m_techniques.cend())) {
qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
- qUtf16PrintableImpl(coreTechniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- } else {
- coreTechnique = it.value();
- coreTechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
- coreTechnique->graphicsApiFilter()->setMajorVersion(3);
- coreTechnique->graphicsApiFilter()->setMinorVersion(1);
- coreTechnique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile);
+ qUtf16PrintableImpl(techniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return NULL;
}
- }
- //Optional GL2 technique
- QString gl2TechniqueName = jsonObj.value(KEY_TECHNIQUE_GL2).toString();
- if (!gl2TechniqueName.isNull()) {
- const auto it = qAsConst(m_techniques).find(gl2TechniqueName);
- if (Q_UNLIKELY(it == m_techniques.cend())) {
- qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
- qUtf16PrintableImpl(gl2TechniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- } else {
- gl2Technique = it.value();
- gl2Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
- gl2Technique->graphicsApiFilter()->setMajorVersion(2);
- gl2Technique->graphicsApiFilter()->setMinorVersion(0);
- gl2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ QTechnique *technique = *it;
+ technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGLES);
+ technique->graphicsApiFilter()->setMajorVersion(2);
+ technique->graphicsApiFilter()->setMinorVersion(0);
+ technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+
+ //Optional Core technique
+ QTechnique *coreTechnique = nullptr;
+ QTechnique *gl2Technique = nullptr;
+ QString coreTechniqueName = jsonObj.value(KEY_TECHNIQUE_CORE).toString();
+ if (!coreTechniqueName.isNull()) {
+ const auto it = qAsConst(m_techniques).find(coreTechniqueName);
+ if (Q_UNLIKELY(it == m_techniques.cend())) {
+ qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
+ qUtf16PrintableImpl(coreTechniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ } else {
+ coreTechnique = it.value();
+ coreTechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
+ coreTechnique->graphicsApiFilter()->setMajorVersion(3);
+ coreTechnique->graphicsApiFilter()->setMinorVersion(1);
+ coreTechnique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile);
+ }
}
- }
-
-
- // 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(techniqueName);
- effect->addTechnique(technique);
- if (coreTechnique != nullptr)
- effect->addTechnique(coreTechnique);
- if (gl2Technique != nullptr)
- effect->addTechnique(gl2Technique);
-
- QMaterial* mat = new QMaterial;
- mat->setEffect(effect);
-
- renameFromJson(jsonObj, mat);
-
- const QJsonObject values = jsonObj.value(KEY_VALUES).toObject();
- for (auto it = values.begin(), end = values.end(); it != end; ++it) {
- const QString vName = it.key();
- QParameter *param = parameterFromTechnique(technique, vName);
-
- if (param == nullptr && coreTechnique != nullptr) {
- param = parameterFromTechnique(coreTechnique, vName);
+ //Optional GL2 technique
+ QString gl2TechniqueName = jsonObj.value(KEY_TECHNIQUE_GL2).toString();
+ if (!gl2TechniqueName.isNull()) {
+ const auto it = qAsConst(m_techniques).find(gl2TechniqueName);
+ if (Q_UNLIKELY(it == m_techniques.cend())) {
+ qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
+ qUtf16PrintableImpl(gl2TechniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ } else {
+ gl2Technique = it.value();
+ gl2Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
+ gl2Technique->graphicsApiFilter()->setMajorVersion(2);
+ gl2Technique->graphicsApiFilter()->setMinorVersion(0);
+ gl2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ }
}
- if (param == nullptr && gl2Technique != nullptr) {
- param = parameterFromTechnique(gl2Technique, vName);
- }
+ // 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(techniqueName);
+ effect->addTechnique(technique);
+ if (coreTechnique != nullptr)
+ effect->addTechnique(coreTechnique);
+ if (gl2Technique != nullptr)
+ effect->addTechnique(gl2Technique);
+
+ QMaterial *mat = new QMaterial;
+ mat->setEffect(effect);
+
+ renameFromJson(jsonObj, mat);
+
+ const QJsonObject values = jsonObj.value(KEY_VALUES).toObject();
+ for (auto it = values.begin(), end = values.end(); it != end; ++it) {
+ const QString vName = it.key();
+ QParameter *param = parameterFromTechnique(technique, vName);
+
+ if (param == nullptr && coreTechnique != nullptr)
+ param = parameterFromTechnique(coreTechnique, vName);
+
+ if (param == nullptr && gl2Technique != nullptr)
+ param = parameterFromTechnique(gl2Technique, vName);
+
+ if (Q_UNLIKELY(!param)) {
+ qCWarning(GLTFImporterLog, "unknown parameter: %ls in technique %ls processing material %ls",
+ qUtf16PrintableImpl(vName), qUtf16PrintableImpl(techniqueName), qUtf16PrintableImpl(id));
+ continue;
+ }
- if (Q_UNLIKELY(!param)) {
- qCWarning(GLTFImporterLog, "unknown parameter: %ls in technique %ls processing material %ls",
- qUtf16PrintableImpl(vName), qUtf16PrintableImpl(techniqueName), qUtf16PrintableImpl(id));
- continue;
- }
+ ParameterData paramData = m_parameterDataDict.value(param);
+ QVariant var = parameterValueFromJSON(paramData.type, it.value());
- ParameterData paramData = m_parameterDataDict.value(param);
- QVariant var = parameterValueFromJSON(paramData.type, it.value());
+ mat->addParameter(new QParameter(param->name(), var));
+ } // of material technique-instance values iteration
- mat->addParameter(new QParameter(param->name(), var));
- } // of material technique-instance values iteration
+ return mat;
+ } else {
+ // Qt3D exported QGLTF custom material
+ QMaterial *mat = new QMaterial;
+ renameFromJson(jsonObj, mat);
+ QEffect *effect = m_effects.value(effectName);
+ if (effect) {
+ mat->setEffect(effect);
+ } else {
+ qCWarning(GLTFImporterLog, "Effect %ls missing for material %ls",
+ qUtf16PrintableImpl(effectName), qUtf16PrintableImpl(mat->objectName()));
+ }
+ const QJsonObject params = jsonObj.value(KEY_PARAMETERS).toObject();
+ for (auto it = params.begin(), end = params.end(); it != end; ++it)
+ mat->addParameter(buildParameter(it.key(), it.value().toObject()));
- return mat;
+ return mat;
+ }
}
QMaterial *GLTFImporter::commonMaterial(const QJsonObject &jsonObj)
@@ -941,10 +994,6 @@ void GLTFImporter::parse()
for (auto it = programs.begin(), end = programs.end(); it != end; ++it)
processJSONProgram(it.key(), it.value().toObject());
- const QJsonObject techniques = m_json.object().value(KEY_TECHNIQUES).toObject();
- for (auto it = techniques.begin(), end = techniques.end(); it != end; ++it)
- processJSONTechnique(it.key(), it.value().toObject());
-
const QJsonObject attrs = m_json.object().value(KEY_ACCESSORS).toObject();
for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it)
processJSONAccessor(it.key(), it.value().toObject());
@@ -965,6 +1014,18 @@ void GLTFImporter::parse()
for (auto it = extensions.begin(), end = extensions.end(); it != end; ++it)
processJSONExtensions(it.key(), it.value().toObject());
+ const QJsonObject passes = m_json.object().value(KEY_RENDERPASSES).toObject();
+ for (auto it = passes.begin(), end = passes.end(); it != end; ++it)
+ processJSONRenderPass(it.key(), it.value().toObject());
+
+ const QJsonObject techniques = m_json.object().value(KEY_TECHNIQUES).toObject();
+ for (auto it = techniques.begin(), end = techniques.end(); it != end; ++it)
+ processJSONTechnique(it.key(), it.value().toObject());
+
+ const QJsonObject effects = m_json.object().value(KEY_EFFECTS).toObject();
+ for (auto it = effects.begin(), end = effects.end(); it != end; ++it)
+ processJSONEffect(it.key(), it.value().toObject());
+
m_defaultScene = m_json.object().value(KEY_SCENE).toString();
m_parseDone = true;
}
@@ -992,6 +1053,9 @@ void GLTFImporter::cleanup()
m_shaderPaths.clear();
delete_if_without_parent(m_programs);
m_programs.clear();
+ for (auto params : m_techniqueParameters.values())
+ delete_if_without_parent(params);
+ m_techniqueParameters.clear();
delete_if_without_parent(m_techniques);
m_techniques.clear();
delete_if_without_parent(m_textures);
@@ -999,6 +1063,10 @@ void GLTFImporter::cleanup()
m_imagePaths.clear();
m_defaultScene.clear();
m_parameterDataDict.clear();
+ delete_if_without_parent(m_renderPasses);
+ m_renderPasses.clear();
+ delete_if_without_parent(m_effects);
+ m_effects.clear();
}
void GLTFImporter::processJSONBuffer(const QString &id, const QJsonObject& json)
@@ -1068,10 +1136,12 @@ void GLTFImporter::processJSONShader(const QString &id, const QJsonObject &jsonO
void GLTFImporter::processJSONProgram(const QString &id, const QJsonObject &jsonObject)
{
- QString fragName = jsonObject.value(KEY_FRAGMENT_SHADER).toString(),
- vertName = jsonObject.value(KEY_VERTEX_SHADER).toString();
- const auto fragIt = qAsConst(m_shaderPaths).find(fragName),
- vertIt = qAsConst(m_shaderPaths).find(vertName);
+ const QString fragName = jsonObject.value(KEY_FRAGMENT_SHADER).toString();
+ const QString vertName = jsonObject.value(KEY_VERTEX_SHADER).toString();
+
+ const auto fragIt = qAsConst(m_shaderPaths).find(fragName);
+ const auto vertIt = qAsConst(m_shaderPaths).find(vertName);
+
if (Q_UNLIKELY(fragIt == m_shaderPaths.cend() || vertIt == m_shaderPaths.cend())) {
qCWarning(GLTFImporterLog, "program: %ls missing shader: %ls %ls",
qUtf16PrintableImpl(id), qUtf16PrintableImpl(fragName), qUtf16PrintableImpl(vertName));
@@ -1082,6 +1152,33 @@ void GLTFImporter::processJSONProgram(const QString &id, const QJsonObject &json
prog->setObjectName(id);
prog->setFragmentShaderCode(QShaderProgram::loadSource(QUrl::fromLocalFile(fragIt.value())));
prog->setVertexShaderCode(QShaderProgram::loadSource(QUrl::fromLocalFile(vertIt.value())));
+
+ const QString tessCtrlName = jsonObject.value(KEY_TESS_CTRL_SHADER).toString();
+ if (!tessCtrlName.isEmpty()) {
+ const auto it = qAsConst(m_shaderPaths).find(tessCtrlName);
+ prog->setTessellationControlShaderCode(
+ QShaderProgram::loadSource(QUrl::fromLocalFile(it.value())));
+ }
+
+ const QString tessEvalName = jsonObject.value(KEY_TESS_EVAL_SHADER).toString();
+ if (!tessEvalName.isEmpty()) {
+ const auto it = qAsConst(m_shaderPaths).find(tessEvalName);
+ prog->setTessellationEvaluationShaderCode(
+ QShaderProgram::loadSource(QUrl::fromLocalFile(it.value())));
+ }
+
+ const QString geomName = jsonObject.value(KEY_GEOMETRY_SHADER).toString();
+ if (!geomName.isEmpty()) {
+ const auto it = qAsConst(m_shaderPaths).find(geomName);
+ prog->setGeometryShaderCode(QShaderProgram::loadSource(QUrl::fromLocalFile(it.value())));
+ }
+
+ const QString computeName = jsonObject.value(KEY_COMPUTE_SHADER).toString();
+ if (!computeName.isEmpty()) {
+ const auto it = qAsConst(m_shaderPaths).find(computeName);
+ prog->setComputeShaderCode(QShaderProgram::loadSource(QUrl::fromLocalFile(it.value())));
+ }
+
m_programs[id] = prog;
}
@@ -1090,115 +1187,121 @@ void GLTFImporter::processJSONTechnique(const QString &id, const QJsonObject &js
QTechnique *t = new QTechnique;
t->setObjectName(id);
- // Parameters
- QHash<QString, QParameter*> paramDict;
- const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
- for (auto it = params.begin(), end = params.end(); it != end; ++it) {
- const QString pname = it.key();
- const QJsonObject po = it.value().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));
+ const QJsonObject gabifilter = jsonObject.value(KEY_GABIFILTER).toObject();
+ if (gabifilter.isEmpty()) {
+ // Regular GLTF technique
+
+ // Parameters
+ QHash<QString, QParameter *> paramDict;
+ const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
+ for (auto it = params.begin(), end = params.end(); it != end; ++it) {
+ const QString pname = it.key();
+ const QJsonObject po = it.value().toObject();
+ QParameter *p = buildParameter(pname, po);
+ m_parameterDataDict.insert(p, ParameterData(po));
+ // We don't want to insert invalid parameters to techniques themselves, but we
+ // need to maintain link between all parameters and techniques so we can resolve
+ // parameter type later when creating materials.
+ if (p->value().isValid())
+ t->addParameter(p);
+ paramDict[pname] = p;
}
- t->addParameter(p);
+ // Program
+ QRenderPass *pass = new QRenderPass;
+ addProgramToPass(pass, jsonObject.value(KEY_PROGRAM).toString());
- paramDict[pname] = p;
- } // of parameters iteration
-
- // Program
- QRenderPass* pass = new QRenderPass;
- QString programName = jsonObject.value(KEY_PROGRAM).toString();
- const auto progIt = qAsConst(m_programs).find(programName);
- if (Q_UNLIKELY(progIt == m_programs.cend())) {
- qCWarning(GLTFImporterLog, "technique %ls: missing program %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(programName));
- } else {
- pass->setShaderProgram(progIt.value());
- }
-
- // Attributes
- const QJsonObject attrs = jsonObject.value(KEY_ATTRIBUTES).toObject();
- for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
- QString pname = it.value().toString();
- QParameter *parameter = paramDict.value(pname, nullptr);
- QString attributeName = pname;
- if (Q_UNLIKELY(!parameter)) {
- qCWarning(GLTFImporterLog, "attribute %ls defined in instanceProgram but not as parameter",
- qUtf16PrintableImpl(pname));
- continue;
- }
- //Check if the parameter has a standard attribute semantic
- const auto paramDataIt = m_parameterDataDict.find(parameter);
- QString standardAttributeName = standardAttributeNameFromSemantic(paramDataIt->semantic);
- if (!standardAttributeName.isNull()) {
- attributeName = standardAttributeName;
- t->removeParameter(parameter);
- m_parameterDataDict.erase(paramDataIt);
- delete parameter;
- }
-
- } // of program-instance attributes
+ // Attributes
+ const QJsonObject attrs = jsonObject.value(KEY_ATTRIBUTES).toObject();
+ for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
+ QString pname = it.value().toString();
+ QParameter *parameter = paramDict.value(pname, nullptr);
+ QString attributeName = pname;
+ if (Q_UNLIKELY(!parameter)) {
+ qCWarning(GLTFImporterLog, "attribute %ls defined in instanceProgram but not as parameter",
+ qUtf16PrintableImpl(pname));
+ continue;
+ }
+ //Check if the parameter has a standard attribute semantic
+ const auto paramDataIt = m_parameterDataDict.find(parameter);
+ QString standardAttributeName = standardAttributeNameFromSemantic(paramDataIt->semantic);
+ if (!standardAttributeName.isNull()) {
+ attributeName = standardAttributeName;
+ t->removeParameter(parameter);
+ m_parameterDataDict.erase(paramDataIt);
+ paramDict.remove(pname);
+ delete parameter;
+ }
- // Uniforms
- const QJsonObject uniforms = jsonObject.value(KEY_UNIFORMS).toObject();
- for (auto it = uniforms.begin(), end = uniforms.end(); it != end; ++it) {
- const QString pname = it.value().toString();
- QParameter *parameter = paramDict.value(pname, nullptr);
- if (Q_UNLIKELY(!parameter)) {
- qCWarning(GLTFImporterLog, "uniform %ls defined in instanceProgram but not as parameter",
- qUtf16PrintableImpl(pname));
- continue;
- }
- //Check if the parameter has a standard uniform semantic
- const auto paramDataIt = m_parameterDataDict.find(parameter);
- if (hasStandardUniformNameFromSemantic(paramDataIt->semantic)) {
- t->removeParameter(parameter);
- m_parameterDataDict.erase(paramDataIt);
- delete parameter;
- }
- } // of program-instance uniforms
+ } // of program-instance attributes
+ // Uniforms
+ const QJsonObject uniforms = jsonObject.value(KEY_UNIFORMS).toObject();
+ for (auto it = uniforms.begin(), end = uniforms.end(); it != end; ++it) {
+ const QString pname = it.value().toString();
+ QParameter *parameter = paramDict.value(pname, nullptr);
+ if (Q_UNLIKELY(!parameter)) {
+ qCWarning(GLTFImporterLog, "uniform %ls defined in instanceProgram but not as parameter",
+ qUtf16PrintableImpl(pname));
+ continue;
+ }
+ //Check if the parameter has a standard uniform semantic
+ const auto paramDataIt = m_parameterDataDict.find(parameter);
+ if (hasStandardUniformNameFromSemantic(paramDataIt->semantic)) {
+ t->removeParameter(parameter);
+ m_parameterDataDict.erase(paramDataIt);
+ paramDict.remove(pname);
+ delete parameter;
+ }
+ } // of program-instance uniforms
- // States
- QJsonObject states = jsonObject.value(KEY_STATES).toObject();
+ m_techniqueParameters.insert(t, paramDict.values());
- //Process states to enable
- const QJsonArray enableStatesArray = states.value(KEY_ENABLE).toArray();
- QVector<int> enableStates;
- for (const QJsonValue &enableValue : enableStatesArray)
- enableStates.append(enableValue.toInt());
+ populateRenderStates(pass, jsonObject.value(KEY_STATES).toObject());
- //Process the list of state functions
- const QJsonObject functions = states.value(KEY_FUNCTIONS).toObject();
- for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
- int enableStateType = 0;
- QRenderState *renderState = buildState(it.key(), it.value(), enableStateType);
- if (renderState != nullptr) {
- //Remove the need to set a default state values for enableStateType
- enableStates.removeOne(enableStateType);
- pass->addRenderState(renderState);
+ t->addRenderPass(pass);
+ } else {
+ // Qt3D exported custom technique
+
+ // Graphics API filter
+ t->graphicsApiFilter()->setApi(QGraphicsApiFilter::Api(gabifilter.value(KEY_API).toInt()));
+ t->graphicsApiFilter()->setMajorVersion(gabifilter.value(KEY_MAJORVERSION).toInt());
+ t->graphicsApiFilter()->setMinorVersion(gabifilter.value(KEY_MINORVERSION).toInt());
+ t->graphicsApiFilter()->setProfile(QGraphicsApiFilter::OpenGLProfile(
+ gabifilter.value(KEY_PROFILE).toInt()));
+ t->graphicsApiFilter()->setVendor(gabifilter.value(KEY_VENDOR).toString());
+ QStringList extensionList;
+ QJsonArray extArray = gabifilter.value(KEY_EXTENSIONS).toArray();
+ for (const QJsonValue &extValue : extArray)
+ extensionList << extValue.toString();
+ t->graphicsApiFilter()->setExtensions(extensionList);
+
+ // Filter keys (we will assume filter keys are always strings or integers)
+ const QJsonObject fkObj = jsonObject.value(KEY_FILTERKEYS).toObject();
+ for (auto it = fkObj.begin(), end = fkObj.end(); it != end; ++it)
+ t->addFilterKey(buildFilterKey(it.key(), it.value()));
+
+ t->setObjectName(jsonObject.value(KEY_NAME).toString());
+
+ // Parameters
+ const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
+ for (auto it = params.begin(), end = params.end(); it != end; ++it)
+ t->addParameter(buildParameter(it.key(), it.value().toObject()));
+
+ // Render passes
+ const QJsonArray passArray = jsonObject.value(KEY_RENDERPASSES).toArray();
+ for (const QJsonValue &passValue : passArray) {
+ const QString passName = passValue.toString();
+ QRenderPass *pass = m_renderPasses.value(passName);
+ if (pass) {
+ t->addRenderPass(pass);
+ } else {
+ qCWarning(GLTFImporterLog, "Render pass %ls missing for technique %ls",
+ qUtf16PrintableImpl(passName), qUtf16PrintableImpl(id));
+ }
}
}
- //Create render states with default values for any remaining enable states
- for (int enableState : qAsConst(enableStates)) {
- QRenderState *renderState = buildStateEnable(enableState);
- if (renderState != nullptr)
- pass->addRenderState(renderState);
- }
-
-
- t->addRenderPass(pass);
-
m_techniques[id] = t;
}
@@ -1423,6 +1526,49 @@ void GLTFImporter::processJSONExtensions(const QString &id, const QJsonObject &j
}
+void GLTFImporter::processJSONEffect(const QString &id, const QJsonObject &jsonObject)
+{
+ QEffect *effect = new QEffect;
+ renameFromJson(jsonObject, effect);
+
+ const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
+ for (auto it = params.begin(), end = params.end(); it != end; ++it)
+ effect->addParameter(buildParameter(it.key(), it.value().toObject()));
+
+ const QJsonArray techArray = jsonObject.value(KEY_TECHNIQUES).toArray();
+ for (const QJsonValue &techValue : techArray) {
+ const QString techName = techValue.toString();
+ QTechnique *tech = m_techniques.value(techName);
+ if (tech) {
+ effect->addTechnique(tech);
+ } else {
+ qCWarning(GLTFImporterLog, "Technique pass %ls missing for effect %ls",
+ qUtf16PrintableImpl(techName), qUtf16PrintableImpl(id));
+ }
+ }
+
+ m_effects[id] = effect;
+}
+
+void GLTFImporter::processJSONRenderPass(const QString &id, const QJsonObject &jsonObject)
+{
+ QRenderPass *pass = new QRenderPass;
+ const QJsonObject passFkObj = jsonObject.value(KEY_FILTERKEYS).toObject();
+ for (auto it = passFkObj.begin(), end = passFkObj.end(); it != end; ++it)
+ pass->addFilterKey(buildFilterKey(it.key(), it.value()));
+
+ const QJsonObject params = jsonObject.value(KEY_PARAMETERS).toObject();
+ for (auto it = params.begin(), end = params.end(); it != end; ++it)
+ pass->addParameter(buildParameter(it.key(), it.value().toObject()));
+
+ populateRenderStates(pass, jsonObject.value(KEY_STATES).toObject());
+ addProgramToPass(pass, jsonObject.value(KEY_PROGRAM).toString());
+
+ renameFromJson(jsonObject, pass);
+
+ m_renderPasses[id] = pass;
+}
+
void GLTFImporter::loadBufferData()
{
for (auto &bufferData : m_bufferDatas) {
@@ -1485,6 +1631,8 @@ QVariant GLTFImporter::parameterValueFromJSON(int type, const QJsonValue &value)
return QVariant(static_cast<GLuint>(value.toInt()));
case GL_FLOAT:
return QVariant(static_cast<GLfloat>(value.toDouble()));
+ default:
+ break;
}
} else if (value.isArray()) {
@@ -1650,29 +1798,28 @@ QRenderState *GLTFImporter::buildStateEnable(int state)
//By calling buildState with QJsonValue(), a Render State with
//default values is created.
- if (state == GL_BLEND) {
+ switch (state) {
+ case GL_BLEND:
//It doesn't make sense to handle this state alone
return nullptr;
- }
-
- if (state == GL_CULL_FACE) {
+ case GL_CULL_FACE:
return buildState(QStringLiteral("cullFace"), QJsonValue(), type);
- }
-
- if (state == GL_DEPTH_TEST) {
+ case GL_DEPTH_TEST:
return buildState(QStringLiteral("depthFunc"), QJsonValue(), type);
- }
-
- if (state == GL_POLYGON_OFFSET_FILL) {
+ case GL_POLYGON_OFFSET_FILL:
return buildState(QStringLiteral("polygonOffset"), QJsonValue(), type);
- }
-
- if (state == GL_SAMPLE_ALPHA_TO_COVERAGE) {
+ case GL_SAMPLE_ALPHA_TO_COVERAGE:
return new QAlphaCoverage();
- }
-
- if (state == GL_SCISSOR_TEST) {
+ case GL_SCISSOR_TEST:
return buildState(QStringLiteral("scissor"), QJsonValue(), type);
+ case GL_DITHER: // Qt3D Custom
+ return new QDithering();
+ case 0x809D: // GL_MULTISAMPLE - Qt3D Custom
+ return new QMultiSampleAntiAliasing();
+ case 0x884F: // GL_TEXTURE_CUBE_MAP_SEAMLESS - Qt3D Custom
+ return new QSeamlessCubemap();
+ default:
+ break;
}
qCWarning(GLTFImporterLog, "unsupported render state: %d", state);
@@ -1707,6 +1854,7 @@ QRenderState* GLTFImporter::buildState(const QString& functionName, const QJsonV
blendArgs->setSourceAlpha((QBlendEquationArguments::Blending)values.at(1).toInt(GL_ONE));
blendArgs->setDestinationRgb((QBlendEquationArguments::Blending)values.at(2).toInt(GL_ZERO));
blendArgs->setDestinationAlpha((QBlendEquationArguments::Blending)values.at(3).toInt(GL_ZERO));
+ blendArgs->setBufferIndex(values.at(4).toInt(-1));
return blendArgs;
}
@@ -1777,10 +1925,132 @@ QRenderState* GLTFImporter::buildState(const QString& functionName, const QJsonV
return scissorTest;
}
+ // Qt3D custom functions
+ if (functionName == QLatin1String("alphaTest")) {
+ QAlphaTest *args = new QAlphaTest;
+ args->setAlphaFunction(QAlphaTest::AlphaFunction(values.at(0).toInt()));
+ args->setReferenceValue(float(values.at(1).toDouble()));
+ return args;
+ }
+
+ if (functionName == QLatin1String("clipPlane")) {
+ QClipPlane *args = new QClipPlane;
+ args->setPlaneIndex(values.at(0).toInt());
+ args->setNormal(QVector3D(float(values.at(1).toDouble()),
+ float(values.at(2).toDouble()),
+ float(values.at(3).toDouble())));
+ args->setDistance(float(values.at(4).toDouble()));
+ return args;
+ }
+
+ if (functionName == QLatin1String("pointSize")) {
+ QPointSize *pointSize = new QPointSize;
+ pointSize->setSizeMode(QPointSize::SizeMode(values.at(0).toInt(QPointSize::Programmable)));
+ pointSize->setValue(float(values.at(1).toDouble()));
+ return pointSize;
+ }
+
+ if (functionName == QLatin1String("stencilMask")) {
+ QStencilMask *stencilMask = new QStencilMask;
+ stencilMask->setFrontOutputMask(uint(values.at(0).toInt()));
+ stencilMask->setBackOutputMask(uint(values.at(1).toInt()));
+ return stencilMask;
+ }
+
+ if (functionName == QLatin1String("stencilOperation")) {
+ QStencilOperation *stencilOperation = new QStencilOperation;
+ stencilOperation->front()->setStencilTestFailureOperation(
+ QStencilOperationArguments::Operation(values.at(0).toInt(
+ QStencilOperationArguments::Keep)));
+ stencilOperation->front()->setDepthTestFailureOperation(
+ QStencilOperationArguments::Operation(values.at(1).toInt(
+ QStencilOperationArguments::Keep)));
+ stencilOperation->front()->setAllTestsPassOperation(
+ QStencilOperationArguments::Operation(values.at(2).toInt(
+ QStencilOperationArguments::Keep)));
+ stencilOperation->back()->setStencilTestFailureOperation(
+ QStencilOperationArguments::Operation(values.at(3).toInt(
+ QStencilOperationArguments::Keep)));
+ stencilOperation->back()->setDepthTestFailureOperation(
+ QStencilOperationArguments::Operation(values.at(4).toInt(
+ QStencilOperationArguments::Keep)));
+ stencilOperation->back()->setAllTestsPassOperation(
+ QStencilOperationArguments::Operation(values.at(5).toInt(
+ QStencilOperationArguments::Keep)));
+ return stencilOperation;
+ }
+
+ if (functionName == QLatin1String("stencilTest")) {
+ QStencilTest *stencilTest = new QStencilTest;
+ stencilTest->front()->setComparisonMask(uint(values.at(0).toInt()));
+ stencilTest->front()->setReferenceValue(values.at(1).toInt());
+ stencilTest->front()->setStencilFunction(
+ QStencilTestArguments::StencilFunction(values.at(2).toInt(
+ QStencilTestArguments::Never)));
+ stencilTest->back()->setComparisonMask(uint(values.at(3).toInt()));
+ stencilTest->back()->setReferenceValue(values.at(4).toInt());
+ stencilTest->back()->setStencilFunction(
+ QStencilTestArguments::StencilFunction(values.at(5).toInt(
+ QStencilTestArguments::Never)));
+ return stencilTest;
+ }
+
qCWarning(GLTFImporterLog, "unsupported render state: %ls", qUtf16PrintableImpl(functionName));
return nullptr;
}
+QParameter *GLTFImporter::buildParameter(const QString &key, const QJsonObject &paramObj)
+{
+ QParameter *p = new QParameter;
+ p->setName(key);
+ QJsonValue value = paramObj.value(KEY_VALUE);
+
+ if (!value.isUndefined()) {
+ int dataType = paramObj.value(KEY_TYPE).toInt();
+ p->setValue(parameterValueFromJSON(dataType, value));
+ }
+
+ return p;
+}
+
+void GLTFImporter::populateRenderStates(QRenderPass *pass, const QJsonObject &states)
+{
+ // Process states to enable
+ const QJsonArray enableStatesArray = states.value(KEY_ENABLE).toArray();
+ QVector<int> enableStates;
+ for (const QJsonValue &enableValue : enableStatesArray)
+ enableStates.append(enableValue.toInt());
+
+ // Process the list of state functions
+ const QJsonObject functions = states.value(KEY_FUNCTIONS).toObject();
+ for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
+ int enableStateType = 0;
+ QRenderState *renderState = buildState(it.key(), it.value(), enableStateType);
+ if (renderState != 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
+ for (int enableState : qAsConst(enableStates)) {
+ QRenderState *renderState = buildStateEnable(enableState);
+ if (renderState != nullptr)
+ pass->addRenderState(renderState);
+ }
+}
+
+void GLTFImporter::addProgramToPass(QRenderPass *pass, const QString &progName)
+{
+ const auto progIt = qAsConst(m_programs).find(progName);
+ if (Q_UNLIKELY(progIt == m_programs.cend()))
+ qCWarning(GLTFImporterLog, "missing program %ls", qUtf16PrintableImpl(progName));
+ else
+ pass->setShaderProgram(progIt.value());
+}
+
+
} // namespace Qt3DRender
QT_END_NAMESPACE
diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.h b/src/plugins/sceneparsers/gltf/gltfimporter.h
index 5033c4f05..8f6b9b356 100644
--- a/src/plugins/sceneparsers/gltf/gltfimporter.h
+++ b/src/plugins/sceneparsers/gltf/gltfimporter.h
@@ -52,12 +52,13 @@
// We mean it.
//
-#include <QtCore/QJsonDocument>
-#include <QtCore/QMultiHash>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qhash.h>
#include <Qt3DRender/qattribute.h>
#include <Qt3DRender/qbuffer.h>
-#include <Qt3DRender/private/qsceneimporter_p.h>
+#include <private/qsceneimporter_p.h>
QT_BEGIN_NAMESPACE
@@ -80,6 +81,7 @@ class QTechnique;
class QParameter;
class QGeometryRenderer;
class QAbstractLight;
+class QRenderPass;
Q_DECLARE_LOGGING_CATEGORY(GLTFImporterLog)
@@ -141,7 +143,7 @@ private:
static void renameFromJson(const QJsonObject& json, QObject * const object );
static bool hasStandardUniformNameFromSemantic(const QString &semantic);
static QString standardAttributeNameFromSemantic(const QString &semantic);
- static QParameter *parameterFromTechnique(QTechnique *technique, const QString &parameterName);
+ QParameter *parameterFromTechnique(QTechnique *technique, const QString &parameterName);
Qt3DCore::QEntity *defaultScene();
QMaterial *material(const QString &id);
@@ -160,6 +162,8 @@ private:
void processJSONImage(const QString &id, const QJsonObject &jsonObject);
void processJSONTexture(const QString &id, const QJsonObject &jsonObject);
void processJSONExtensions(const QString &id, const QJsonObject &jsonObject);
+ void processJSONEffect(const QString &id, const QJsonObject &jsonObject);
+ void processJSONRenderPass(const QString &id, const QJsonObject &jsonObject);
void loadBufferData();
void unloadBufferData();
@@ -172,6 +176,9 @@ private:
static QRenderState *buildStateEnable(int state);
static QRenderState *buildState(const QString& functionName, const QJsonValue &value, int &type);
+ QParameter *buildParameter(const QString &key, const QJsonObject &paramObj);
+ void populateRenderStates(QRenderPass *pass, const QJsonObject &states);
+ void addProgramToPass(QRenderPass *pass, const QString &progName);
QMaterial *materialWithCustomShader(const QString &id, const QJsonObject &jsonObj);
QMaterial *commonMaterial(const QJsonObject &jsonObj);
@@ -200,6 +207,9 @@ private:
QHash<QString, QShaderProgram*> m_programs;
QHash<QString, QTechnique *> m_techniques;
+ QHash<QString, QRenderPass *> m_renderPasses;
+ QHash<QString, QEffect *> m_effects;
+ QHash<QTechnique *, QList<QParameter *> > m_techniqueParameters;
QHash<QParameter*, ParameterData> m_parameterDataDict;
QHash<QString, QAbstractTexture*> m_textures;
diff --git a/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp b/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp
index e9f1a194e..61a448666 100644
--- a/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp
+++ b/src/plugins/sceneparsers/gltfexport/gltfexporter.cpp
@@ -77,6 +77,26 @@
#include <Qt3DRender/qgeometry.h>
#include <Qt3DRender/qgeometryrenderer.h>
#include <Qt3DRender/qgeometryfactory.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qalphacoverage.h>
+#include <Qt3DRender/qalphatest.h>
+#include <Qt3DRender/qclipplane.h>
+#include <Qt3DRender/qcolormask.h>
+#include <Qt3DRender/qcullface.h>
+#include <Qt3DRender/qdepthtest.h>
+#include <Qt3DRender/qdithering.h>
+#include <Qt3DRender/qfrontface.h>
+#include <Qt3DRender/qmultisampleantialiasing.h>
+#include <Qt3DRender/qnodepthmask.h>
+#include <Qt3DRender/qpointsize.h>
+#include <Qt3DRender/qpolygonoffset.h>
+#include <Qt3DRender/qscissortest.h>
+#include <Qt3DRender/qseamlesscubemap.h>
+#include <Qt3DRender/qstencilmask.h>
+#include <Qt3DRender/qstenciloperation.h>
+#include <Qt3DRender/qstenciloperationarguments.h>
+#include <Qt3DRender/qstenciltest.h>
+#include <Qt3DRender/qstenciltestarguments.h>
#include <Qt3DExtras/qconemesh.h>
#include <Qt3DExtras/qcuboidmesh.h>
#include <Qt3DExtras/qcylindermesh.h>
@@ -95,17 +115,102 @@
#include <private/qurlhelper_p.h>
-#define GLT_UNSIGNED_SHORT 0x1403
-#define GLT_UNSIGNED_INT 0x1405
-#define GLT_FLOAT 0x1406
-#define GLT_ARRAY_BUFFER 0x8892
-#define GLT_ELEMENT_ARRAY_BUFFER 0x8893
-
#ifndef qUtf16PrintableImpl
# define qUtf16PrintableImpl(string) \
static_cast<const wchar_t*>(static_cast<const void*>(string.utf16()))
#endif
+namespace {
+
+inline QJsonArray col2jsvec(const QColor &color, bool alpha = false)
+{
+ QJsonArray arr;
+ arr << color.redF() << color.greenF() << color.blueF();
+ if (alpha)
+ arr << color.alphaF();
+ return arr;
+}
+
+template <typename T>
+inline QJsonArray vec2jsvec(const QVector<T> &v)
+{
+ QJsonArray arr;
+ for (int i = 0; i < v.count(); ++i)
+ arr << v.at(i);
+ return arr;
+}
+
+inline QJsonArray vec2jsvec(const QVector2D &v)
+{
+ QJsonArray arr;
+ arr << v.x() << v.y();
+ return arr;
+}
+
+inline QJsonArray vec2jsvec(const QVector3D &v)
+{
+ QJsonArray arr;
+ arr << v.x() << v.y() << v.z();
+ return arr;
+}
+
+inline QJsonArray vec2jsvec(const QVector4D &v)
+{
+ QJsonArray arr;
+ arr << v.x() << v.y() << v.z() << v.w();
+ return arr;
+}
+
+inline QJsonArray matrix2jsvec(const QMatrix2x2 &matrix)
+{
+ QJsonArray jm;
+ const float *mtxp = matrix.constData();
+ for (int j = 0; j < 4; ++j)
+ jm.append(*mtxp++);
+ return jm;
+}
+
+inline QJsonArray matrix2jsvec(const QMatrix3x3 &matrix)
+{
+ QJsonArray jm;
+ const float *mtxp = matrix.constData();
+ for (int j = 0; j < 9; ++j)
+ jm.append(*mtxp++);
+ return jm;
+}
+
+inline QJsonArray matrix2jsvec(const QMatrix4x4 &matrix)
+{
+ QJsonArray jm;
+ const float *mtxp = matrix.constData();
+ for (int j = 0; j < 16; ++j)
+ jm.append(*mtxp++);
+ return jm;
+}
+
+inline void promoteColorsToRGBA(QJsonObject *obj)
+{
+ auto it = obj->begin();
+ auto itEnd = obj->end();
+ while (it != itEnd) {
+ QJsonArray arr = it.value().toArray();
+ if (arr.count() == 3) {
+ const QString key = it.key();
+ if (key == QStringLiteral("ambient")
+ || key == QStringLiteral("diffuse")
+ || key == QStringLiteral("specular")
+ || key == QStringLiteral("warm")
+ || key == QStringLiteral("cool")) {
+ arr.append(1);
+ *it = arr;
+ }
+ }
+ ++it;
+ }
+}
+
+} // namespace
+
QT_BEGIN_NAMESPACE
using namespace Qt3DCore;
@@ -152,31 +257,6 @@ GLTFExporter::~GLTFExporter()
{
}
-// Calculate bounding box
-void calcBB(QVector<float> &minVal, QVector<float> &maxVal, const float *data,
- int vertexCount, int compCount, int offset, int stride)
-{
- minVal.resize(compCount);
- maxVal.resize(compCount);
- int dataIndex = offset;
- const int adjustedStride = stride > 0 ? stride - compCount : 0;
- for (int i = 0; i < vertexCount; ++i) {
- for (int j = 0; j < compCount; ++j) {
- if (i == 0) {
- minVal[j] = data[dataIndex];
- maxVal[j] = data[dataIndex];
- } else {
- if (data[dataIndex] < minVal[j])
- minVal[j] = data[dataIndex];
- if (data[dataIndex] > maxVal[j])
- maxVal[j] = data[dataIndex];
- }
- dataIndex++;
- }
- dataIndex += adjustedStride;
- }
-}
-
// sceneRoot : The root entity that contains the exported scene. If the sceneRoot doesn't have
// any exportable components, it is not exported itself. This is because importing a
// scene creates an empty top level entity to hold the scene.
@@ -197,11 +277,19 @@ bool GLTFExporter::exportScene(QEntity *sceneRoot, const QString &outDir,
m_materialMap.clear();
m_cameraMap.clear();
m_lightMap.clear();
+ m_transformMap.clear();
m_imageMap.clear();
+ m_textureIdMap.clear();
m_meshInfo.clear();
m_materialInfo.clear();
m_cameraInfo.clear();
+ m_lightInfo.clear();
m_exportedFiles.clear();
+ m_renderPassIdMap.clear();
+ m_shaderInfo.clear();
+ m_programInfo.clear();
+ m_techniqueIdMap.clear();
+ m_effectIdMap.clear();
delNode(m_rootNode);
@@ -217,6 +305,8 @@ bool GLTFExporter::exportScene(QEntity *sceneRoot, const QString &outDir,
m_nodeCount = 0;
m_cameraCount = 0;
m_lightCount = 0;
+ m_renderPassCount = 0;
+ m_effectCount = 0;
m_gltfOpts.binaryJson = options.value(QStringLiteral("binaryJson"),
QVariant(false)).toBool();
@@ -260,16 +350,13 @@ bool GLTFExporter::exportScene(QEntity *sceneRoot, const QString &outDir,
m_exportDir = exportDir.path();
m_exportDir.append(QStringLiteral("/"));
- qCDebug(GLTFExporterLog, "Output directory: &ls", qUtf16PrintableImpl(absoluteOutDir));
- qCDebug(GLTFExporterLog, "Export name: &ls", qUtf16PrintableImpl(m_exportName));
- qCDebug(GLTFExporterLog, "Temp export dir: &ls", qUtf16PrintableImpl(m_exportDir));
- qCDebug(GLTFExporterLog, "Final export dir: &ls", qUtf16PrintableImpl(finalExportDir));
+ qCDebug(GLTFExporterLog, "Output directory: %ls", qUtf16PrintableImpl(absoluteOutDir));
+ qCDebug(GLTFExporterLog, "Export name: %ls", qUtf16PrintableImpl(m_exportName));
+ qCDebug(GLTFExporterLog, "Temp export dir: %ls", qUtf16PrintableImpl(m_exportDir));
+ qCDebug(GLTFExporterLog, "Final export dir: %ls", qUtf16PrintableImpl(finalExportDir));
parseScene();
- // Copy textures into temporary directory
- copyTextures();
-
// Export scene to temporary directory
if (!saveScene()) {
qCWarning(GLTFExporterLog, "Exporting GLTF scene failed");
@@ -327,54 +414,67 @@ void GLTFExporter::copyTextures()
{
qCDebug(GLTFExporterLog, "Copying textures...");
QHash<QString, QString> copiedMap;
- for (auto it = m_materialInfo.constBegin(); it != m_materialInfo.constEnd(); ++it) {
- const MaterialInfo &matInfo = it.value();
- for (auto texIt = matInfo.textures.constBegin(); texIt != matInfo.textures.constEnd();
- ++texIt) {
- QString texKey = texIt.key();
- QFileInfo fi(texIt.value());
- QString absoluteFilePath;
- if (texIt.value().startsWith(QStringLiteral(":")))
- absoluteFilePath = texIt.value();
- else
- absoluteFilePath = fi.absoluteFilePath();
- if (copiedMap.contains(absoluteFilePath)) {
- // Texture has already been copied
- qCDebug(GLTFExporterLog, " Skipped copying duplicate texture: '%ls'",
- qUtf16PrintableImpl(absoluteFilePath));
- if (!m_imageMap.contains(texIt.value()))
- m_imageMap.insert(texIt.value(), copiedMap.value(absoluteFilePath));
- } else {
- QString fileName = fi.fileName();
- QString outFile = m_exportDir;
- outFile.append(fileName);
- QFileInfo fiTry(outFile);
- if (fiTry.exists()) {
- static const QString outFileTemplate = QStringLiteral("%2_%3.%4");
- int counter = 0;
- QString tryFile = outFile;
- QString suffix = fiTry.suffix();
- QString base = fiTry.baseName();
- while (fiTry.exists()) {
- fileName = outFileTemplate.arg(base).arg(counter++).arg(suffix);
- tryFile = m_exportDir;
- tryFile.append(fileName);
- fiTry.setFile(tryFile);
- }
- outFile = tryFile;
- }
- if (!QFile(absoluteFilePath).copy(outFile)) {
- qCWarning(GLTFExporterLog, " Failed to copy texture: '%ls' -> '%ls'",
- qUtf16PrintableImpl(absoluteFilePath), qUtf16PrintableImpl(outFile));
- } else {
- qCDebug(GLTFExporterLog, " Copied texture: '%ls' -> '%ls'",
- qUtf16PrintableImpl(absoluteFilePath), qUtf16PrintableImpl(outFile));
+ for (auto texIt = m_textureIdMap.constBegin(); texIt != m_textureIdMap.constEnd(); ++texIt) {
+ QFileInfo fi(texIt.key());
+ QString absoluteFilePath;
+ if (texIt.key().startsWith(QStringLiteral(":")))
+ absoluteFilePath = texIt.key();
+ else
+ absoluteFilePath = fi.absoluteFilePath();
+ if (copiedMap.contains(absoluteFilePath)) {
+ // Texture has already been copied
+ qCDebug(GLTFExporterLog, " Skipped copying duplicate texture: '%ls'",
+ qUtf16PrintableImpl(absoluteFilePath));
+ if (!m_imageMap.contains(texIt.key()))
+ m_imageMap.insert(texIt.key(), copiedMap.value(absoluteFilePath));
+ } else {
+ QString fileName = fi.fileName();
+ QString outFile = m_exportDir;
+ outFile.append(fileName);
+ QFileInfo fiTry(outFile);
+ if (fiTry.exists()) {
+ static const QString outFileTemplate = QStringLiteral("%2_%3.%4");
+ int counter = 0;
+ QString tryFile = outFile;
+ QString suffix = fiTry.suffix();
+ QString base = fiTry.baseName();
+ while (fiTry.exists()) {
+ fileName = outFileTemplate.arg(base).arg(counter++).arg(suffix);
+ tryFile = m_exportDir;
+ tryFile.append(fileName);
+ fiTry.setFile(tryFile);
}
- // Generate actual target file (as current exportDir is temp dir)
- copiedMap.insert(absoluteFilePath, fileName);
- m_exportedFiles.insert(fileName);
- m_imageMap.insert(texIt.value(), fileName);
+ outFile = tryFile;
+ }
+ if (!QFile(absoluteFilePath).copy(outFile)) {
+ qCWarning(GLTFExporterLog, " Failed to copy texture: '%ls' -> '%ls'",
+ qUtf16PrintableImpl(absoluteFilePath), qUtf16PrintableImpl(outFile));
+ } else {
+ qCDebug(GLTFExporterLog, " Copied texture: '%ls' -> '%ls'",
+ qUtf16PrintableImpl(absoluteFilePath), qUtf16PrintableImpl(outFile));
}
+ // Generate actual target file (as current exportDir is temp dir)
+ copiedMap.insert(absoluteFilePath, fileName);
+ m_exportedFiles.insert(fileName);
+ m_imageMap.insert(texIt.key(), fileName);
+ }
+ }
+}
+
+// Creates shaders to the temporary export directory.
+void GLTFExporter::createShaders()
+{
+ qCDebug(GLTFExporterLog, "Creating shaders...");
+ for (auto si : m_shaderInfo) {
+ const QString fileName = m_exportDir + si.uri;
+ QFile f(fileName);
+ if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
+ m_exportedFiles.insert(QFileInfo(f.fileName()).fileName());
+ f.write(si.code);
+ f.close();
+ } else {
+ qCWarning(GLTFExporterLog, " Writing shaderfile '%ls' failed!",
+ qUtf16PrintableImpl(fileName));
}
}
}
@@ -468,38 +568,36 @@ void GLTFExporter::parseMaterials()
matInfo.type = MaterialInfo::TypePerVertex;
} else {
matInfo.type = MaterialInfo::TypeCustom;
- // TODO: Implement support for custom materials
- qCDebug(GLTFExporterLog, "Exporting custom materials is not supported: %s",
- material->metaObject()->className());
- continue;
}
- if (material->effect()) {
- QVector<QParameter *> parameters = material->effect()->parameters();
- for (auto param : parameters) {
- if (param->value().type() == QVariant::Color) {
- QColor color = param->value().value<QColor>();
- if (param->name() == MATERIAL_AMBIENT_COLOR)
- matInfo.colors.insert(QStringLiteral("ambient"), color);
- else if (param->name() == MATERIAL_DIFFUSE_COLOR)
- matInfo.colors.insert(QStringLiteral("diffuse"), color);
- else if (param->name() == MATERIAL_SPECULAR_COLOR)
- matInfo.colors.insert(QStringLiteral("specular"), color);
- else if (param->name() == MATERIAL_COOL_COLOR) // Custom Qt3D param for gooch
- matInfo.colors.insert(QStringLiteral("cool"), color);
- else if (param->name() == MATERIAL_WARM_COLOR) // Custom Qt3D param for gooch
- matInfo.colors.insert(QStringLiteral("warm"), color);
- else
- matInfo.colors.insert(param->name(), color);
- } else if (param->value().canConvert<QAbstractTexture *>()) {
- QAbstractTexture *texture = param->value().value<QAbstractTexture *>();
- for (auto ti : texture->textureImages()) {
- QString urlString;
- Qt3DRender::QTextureImage *image =
- qobject_cast<Qt3DRender::QTextureImage *>(ti);
- if (image)
- urlString = QUrlHelper::urlToLocalFileOrQrc(image->source());
-
+ if (matInfo.type == MaterialInfo::TypeCustom) {
+ if (material->effect()) {
+ if (!m_effectIdMap.contains(material->effect()))
+ m_effectIdMap.insert(material->effect(), newEffectName());
+ parseTechniques(material);
+ }
+ } else {
+ // Default materials do not have separate effect, all effect parameters are stored as
+ // material values.
+ if (material->effect()) {
+ QVector<QParameter *> parameters = material->effect()->parameters();
+ for (auto param : parameters) {
+ if (param->value().type() == QVariant::Color) {
+ QColor color = param->value().value<QColor>();
+ if (param->name() == MATERIAL_AMBIENT_COLOR)
+ matInfo.colors.insert(QStringLiteral("ambient"), color);
+ else if (param->name() == MATERIAL_DIFFUSE_COLOR)
+ matInfo.colors.insert(QStringLiteral("diffuse"), color);
+ else if (param->name() == MATERIAL_SPECULAR_COLOR)
+ matInfo.colors.insert(QStringLiteral("specular"), color);
+ else if (param->name() == MATERIAL_COOL_COLOR) // Custom Qt3D gooch
+ matInfo.colors.insert(QStringLiteral("cool"), color);
+ else if (param->name() == MATERIAL_WARM_COLOR) // Custom Qt3D gooch
+ matInfo.colors.insert(QStringLiteral("warm"), color);
+ else
+ matInfo.colors.insert(param->name(), color);
+ } else if (param->value().canConvert<QAbstractTexture *>()) {
+ const QString urlString = textureVariantToUrl(param->value());
if (param->name() == MATERIAL_DIFFUSE_TEXTURE)
matInfo.textures.insert(QStringLiteral("diffuse"), urlString);
else if (param->name() == MATERIAL_SPECULAR_TEXTURE)
@@ -508,21 +606,17 @@ void GLTFExporter::parseMaterials()
matInfo.textures.insert(QStringLiteral("normal"), urlString);
else
matInfo.textures.insert(param->name(), urlString);
- }
- } else if (param->name() == MATERIAL_SHININESS) {
- matInfo.values.insert(QStringLiteral("shininess"), param->value());
- } else if (param->name() == MATERIAL_BETA) { // Custom Qt3D param for gooch
- matInfo.values.insert(QStringLiteral("beta"), param->value());
- } else if (param->name() == MATERIAL_ALPHA) {
- if (matInfo.type == MaterialInfo::TypeGooch)
- matInfo.values.insert(QStringLiteral("alpha"), param->value());
- else
- matInfo.values.insert(QStringLiteral("transparency"), param->value());
- } else if (param->name() == MATERIAL_TEXTURE_SCALE) { // Custom Qt3D param
- matInfo.values.insert(QStringLiteral("textureScale"), param->value());
- } else {
- if (matInfo.type == MaterialInfo::TypeCustom) {
- matInfo.values.insert(param->name(), param->value());
+ } else if (param->name() == MATERIAL_SHININESS) {
+ matInfo.values.insert(QStringLiteral("shininess"), param->value());
+ } else if (param->name() == MATERIAL_BETA) { // Custom Qt3D param for gooch
+ matInfo.values.insert(QStringLiteral("beta"), param->value());
+ } else if (param->name() == MATERIAL_ALPHA) {
+ if (matInfo.type == MaterialInfo::TypeGooch)
+ matInfo.values.insert(QStringLiteral("alpha"), param->value());
+ else
+ matInfo.values.insert(QStringLiteral("transparency"), param->value());
+ } else if (param->name() == MATERIAL_TEXTURE_SCALE) { // Custom Qt3D param
+ matInfo.values.insert(QStringLiteral("textureScale"), param->value());
} else {
qCDebug(GLTFExporterLog,
"Common material had unknown parameter: '%ls'",
@@ -531,6 +625,7 @@ void GLTFExporter::parseMaterials()
}
}
}
+
if (GLTFExporterLog().isDebugEnabled()) {
qCDebug(GLTFExporterLog, " Material #%i", materialCount);
qCDebug(GLTFExporterLog, " name: '%ls'", qUtf16PrintableImpl(matInfo.name));
@@ -581,154 +676,87 @@ void GLTFExporter::parseMeshes()
}
QAttribute *indexAttrib = nullptr;
- QAttribute *verticesAttrib = nullptr;
- QAttribute *normalsAttrib = nullptr;
- QAttribute *texCoordsAttrib = nullptr;
- QAttribute *colorAttrib = nullptr;
- QAttribute *tangentsAttrib = nullptr;
-
- const float *vertexPtr = nullptr;
- const float *normalsPtr = nullptr;
- const float *texCoordsPtr = nullptr;
- const float *colorPtr = nullptr;
- const float *tangentsPtr = nullptr;
const quint16 *indexPtr = nullptr;
- for (auto att : meshGeometry->attributes()) {
+ struct VertexAttrib {
+ QAttribute *att;
+ const float *ptr;
+ QString usage;
+ uint offset;
+ uint stride;
+ int index;
+ };
+
+ QVector<VertexAttrib> vAttribs;
+ vAttribs.reserve(meshGeometry->attributes().size());
+
+ uint stride(0);
+
+ for (QAttribute *att : meshGeometry->attributes()) {
if (att->attributeType() == QAttribute::IndexAttribute) {
indexAttrib = att;
indexPtr = reinterpret_cast<const quint16 *>(att->buffer()->data().constData());
- } else if (att->name() == VERTICES_ATTRIBUTE_NAME) {
- verticesAttrib = att;
- vertexPtr = reinterpret_cast<const float *>(att->buffer()->data().constData());
- } else if (att->name() == NORMAL_ATTRIBUTE_NAME) {
- normalsAttrib = att;
- normalsPtr = reinterpret_cast<const float *>(att->buffer()->data().constData());
- } else if (att->name() == TEXTCOORD_ATTRIBUTE_NAME) {
- texCoordsAttrib = att;
- texCoordsPtr = reinterpret_cast<const float *>(att->buffer()->data().constData());
- } else if (att->name() == COLOR_ATTRIBUTE_NAME) {
- colorAttrib = att;
- colorPtr = reinterpret_cast<const float *>(att->buffer()->data().constData());
- } else if (att->name() == TANGENT_ATTRIBUTE_NAME) {
- tangentsAttrib = att;
- tangentsPtr = reinterpret_cast<const float *>(att->buffer()->data().constData());
+ } else {
+ VertexAttrib vAtt;
+ vAtt.att = att;
+ vAtt.ptr = reinterpret_cast<const float *>(att->buffer()->data().constData());
+ if (att->name() == VERTICES_ATTRIBUTE_NAME)
+ vAtt.usage = QStringLiteral("POSITION");
+ else if (att->name() == NORMAL_ATTRIBUTE_NAME)
+ vAtt.usage = QStringLiteral("NORMAL");
+ else if (att->name() == TEXTCOORD_ATTRIBUTE_NAME)
+ vAtt.usage = QStringLiteral("TEXCOORD_0");
+ else if (att->name() == COLOR_ATTRIBUTE_NAME)
+ vAtt.usage = QStringLiteral("COLOR");
+ else if (att->name() == TANGENT_ATTRIBUTE_NAME)
+ vAtt.usage = QStringLiteral("TANGENT");
+ else
+ vAtt.usage = att->name();
+
+ vAtt.offset = att->byteOffset() / sizeof(float);
+ vAtt.index = vAtt.offset;
+ vAtt.stride = att->byteStride() > 0
+ ? att->byteStride() / sizeof(float) - att->vertexSize() : 0;
+ stride += att->vertexSize();
+
+ vAttribs << vAtt;
}
}
- Q_ASSERT(verticesAttrib);
+ int attribCount(vAttribs.size());
+ if (!attribCount) {
+ qCWarning(GLTFExporterLog, "Ignoring mesh without any attributes!");
+ continue;
+ }
// Default types use single interleaved buffer for all vertex data, but we must regenerate
// it as it is not available on the frontend by default.
QByteArray defaultVertexArray;
QByteArray defaultIndexArray;
if (defaultType) {
- defaultVertexArray = verticesAttrib->buffer()->dataGenerator().data()->operator()();
+ defaultVertexArray =
+ vAttribs.at(0).att->buffer()->dataGenerator().data()->operator()();
const float *defaultVertexBufferPtr =
reinterpret_cast<const float *>(defaultVertexArray.constData());
- vertexPtr = defaultVertexBufferPtr;
- if (normalsPtr)
- normalsPtr = defaultVertexBufferPtr;
- if (texCoordsPtr)
- texCoordsPtr = defaultVertexBufferPtr;
- if (colorPtr)
- colorPtr = defaultVertexBufferPtr;
- if (tangentsPtr)
- tangentsPtr = defaultVertexBufferPtr;
+ for (int i = 0; i < attribCount; i++)
+ vAttribs[i].ptr = defaultVertexBufferPtr;
defaultIndexArray = indexAttrib->buffer()->dataGenerator().data()->operator()();
indexPtr = reinterpret_cast<const quint16 *>(defaultIndexArray.constData());
}
- const uint vertexOffset = verticesAttrib->byteOffset() / sizeof(float);
- uint normalOffset = 0;
- uint textCoordOffset = 0;
- uint colorOffset = 0;
- uint tangentOffset = 0;
-
- const uint vertexStride = verticesAttrib->byteStride() > 0
- ? verticesAttrib->byteStride() / sizeof(float) - 3 : 0;
- uint normalStride = 0;
- uint textCoordStride = 0;
- uint colorStride = 0;
- uint tangentStride = 0;
-
- uint stride = 3;
-
- if (normalsAttrib) {
- normalOffset = normalsAttrib->byteOffset() / sizeof(float);
- normalStride = normalsAttrib->byteStride() / sizeof(float);
- if (normalStride > 0)
- normalStride -= 3;
- stride += 3;
- }
- if (texCoordsAttrib) {
- textCoordOffset = texCoordsAttrib->byteOffset() / sizeof(float);
- textCoordStride = texCoordsAttrib->byteStride() / sizeof(float);
- if (textCoordStride > 0)
- textCoordStride -= 2;
- stride += 2;
- }
- if (colorAttrib) {
- colorOffset = colorAttrib->byteOffset() / sizeof(float);
- colorStride = colorAttrib->byteStride() / sizeof(float);
- if (colorStride > 0)
- colorStride -= 4;
- stride += 4;
- }
- if (tangentsAttrib) {
- tangentOffset = tangentsAttrib->byteOffset() / sizeof(float);
- tangentStride = tangentsAttrib->byteStride() / sizeof(float);
- if (tangentStride > 0)
- tangentStride -= 4;
- stride += 4;
- }
-
QByteArray vertexBuf;
- const int vertexCount = verticesAttrib->count();
+ const int vertexCount = vAttribs.at(0).att->count();
vertexBuf.resize(stride * vertexCount * sizeof(float));
float *p = reinterpret_cast<float *>(vertexBuf.data());
- uint vertexIndex = vertexOffset;
- uint normalIndex = normalOffset;
- uint textCoordIndex = textCoordOffset;
- uint colorIndex = colorOffset;
- uint tangentIndex = tangentOffset;
-
// Create interleaved buffer
- for (int j = 0; j < vertexCount; ++j) {
- *p++ = vertexPtr[vertexIndex++];
- *p++ = vertexPtr[vertexIndex++];
- *p++ = vertexPtr[vertexIndex++];
- vertexIndex += vertexStride;
-
- if (normalsPtr) {
- *p++ = normalsPtr[normalIndex++];
- *p++ = normalsPtr[normalIndex++];
- *p++ = normalsPtr[normalIndex++];
- normalIndex += normalStride;
- }
-
- if (texCoordsPtr) {
- *p++ = texCoordsPtr[textCoordIndex++];
- *p++ = texCoordsPtr[textCoordIndex++];
- textCoordIndex += textCoordStride;
- }
-
- if (colorPtr) {
- *p++ = colorPtr[colorIndex++];
- *p++ = colorPtr[colorIndex++];
- *p++ = colorPtr[colorIndex++];
- *p++ = colorPtr[colorIndex++];
- colorIndex += colorStride;
- }
-
- if (tangentsPtr) {
- *p++ = tangentsPtr[tangentIndex++];
- *p++ = tangentsPtr[tangentIndex++];
- *p++ = tangentsPtr[tangentIndex++];
- *p++ = tangentsPtr[tangentIndex++];
- tangentIndex += tangentStride;
+ for (int i = 0; i < vertexCount; ++i) {
+ for (int j = 0; j < attribCount; ++j) {
+ VertexAttrib &vAtt = vAttribs[j];
+ for (uint k = 0; k < vAtt.att->vertexSize(); ++k)
+ *p++ = vAtt.ptr[vAtt.index++];
+ vAtt.index += vAtt.stride;
}
}
@@ -736,8 +764,8 @@ void GLTFExporter::parseMeshes()
vertexBufView.name = newBufferViewName();
vertexBufView.length = vertexBuf.size();
vertexBufView.offset = m_buffer.size();
- vertexBufView.componentType = GLT_FLOAT;
- vertexBufView.target = GLT_ARRAY_BUFFER;
+ vertexBufView.componentType = GL_FLOAT;
+ vertexBufView.target = GL_ARRAY_BUFFER;
meshInfo.views.append(vertexBufView);
QByteArray indexBuf;
@@ -770,60 +798,48 @@ void GLTFExporter::parseMeshes()
indexBufView.length = indexBuf.size();
indexBufView.offset = vertexBufView.offset + vertexBufView.length;
indexBufView.componentType = indexSize == sizeof(quint32)
- ? GLT_UNSIGNED_INT : GLT_UNSIGNED_SHORT;
- indexBufView.target = GLT_ELEMENT_ARRAY_BUFFER;
+ ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
+ indexBufView.target = GL_ELEMENT_ARRAY_BUFFER;
meshInfo.views.append(indexBufView);
}
MeshInfo::Accessor acc;
uint startOffset = 0;
- const float *bufPtr = reinterpret_cast<const float *>(vertexBuf.constData());
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("POSITION");
+
acc.bufferView = vertexBufView.name;
- acc.offset = 0;
acc.stride = stride * sizeof(float);
acc.count = vertexCount;
acc.componentType = vertexBufView.componentType;
- acc.type = QStringLiteral("VEC3");
- calcBB(acc.minVal, acc.maxVal, bufPtr, vertexCount, 3, startOffset, stride);
- meshInfo.accessors.append(acc);
- startOffset += 3;
-
- if (normalsAttrib) {
+ for (int i = 0; i < attribCount; ++i) {
+ const VertexAttrib &vAtt = vAttribs.at(i);
acc.name = newAccessorName();
- acc.usage = QStringLiteral("NORMAL");
+ acc.usage = vAtt.usage;
acc.offset = startOffset * sizeof(float);
- calcBB(acc.minVal, acc.maxVal, bufPtr, vertexCount, 3, startOffset, stride);
- meshInfo.accessors.append(acc);
- startOffset += 3;
- }
- if (texCoordsAttrib) {
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("TEXCOORD_0");
- acc.offset = startOffset * sizeof(float);
- acc.type = QStringLiteral("VEC2");
- calcBB(acc.minVal, acc.maxVal, bufPtr, vertexCount, 2, startOffset, stride);
- meshInfo.accessors.append(acc);
- startOffset += 2;
- }
- if (colorAttrib) {
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("COLOR");
- acc.offset = startOffset * sizeof(float);
- acc.type = QStringLiteral("VEC4");
- calcBB(acc.minVal, acc.maxVal, bufPtr, vertexCount, 4, startOffset, stride);
- meshInfo.accessors.append(acc);
- startOffset += 4;
- }
- if (tangentsAttrib) {
- acc.name = newAccessorName();
- acc.usage = QStringLiteral("TANGENT");
- acc.offset = startOffset * sizeof(float);
- acc.type = QStringLiteral("VEC4");
- calcBB(acc.minVal, acc.maxVal, bufPtr, vertexCount, 4, startOffset, stride);
+ switch (vAtt.att->vertexSize()) {
+ case 1:
+ acc.type = QStringLiteral("SCALAR");
+ break;
+ case 2:
+ acc.type = QStringLiteral("VEC2");
+ break;
+ case 3:
+ acc.type = QStringLiteral("VEC3");
+ break;
+ case 4:
+ acc.type = QStringLiteral("VEC4");
+ break;
+ case 9:
+ acc.type = QStringLiteral("MAT3");
+ break;
+ case 16:
+ acc.type = QStringLiteral("MAT4");
+ break;
+ default:
+ qCWarning(GLTFExporterLog, "Invalid vertex size: %d", vAtt.att->vertexSize());
+ break;
+ }
meshInfo.accessors.append(acc);
- startOffset += 4;
+ startOffset += vAtt.att->vertexSize();
}
// Index
@@ -836,7 +852,6 @@ void GLTFExporter::parseMeshes()
acc.count = indexCount;
acc.componentType = indexBufView.componentType;
acc.type = QStringLiteral("SCALAR");
- acc.minVal = acc.maxVal = QVector<float>();
meshInfo.accessors.append(acc);
}
@@ -903,10 +918,10 @@ void GLTFExporter::parseCameras()
if (GLTFExporterLog().isDebugEnabled()) {
qCDebug(GLTFExporterLog, " Camera: #%i: (%ls/%ls)", cameraCount++,
qUtf16PrintableImpl(c.name), qUtf16PrintableImpl(c.originalName));
- qCDebug(GLTFExporterLog, " Aspect ratio: %i", c.aspectRatio);
- qCDebug(GLTFExporterLog, " Fov: %i", c.yfov);
- qCDebug(GLTFExporterLog, " Near: %i", c.znear);
- qCDebug(GLTFExporterLog, " Far: %i", c.zfar);
+ qCDebug(GLTFExporterLog, " Aspect ratio: %f", c.aspectRatio);
+ qCDebug(GLTFExporterLog, " Fov: %f", c.yfov);
+ qCDebug(GLTFExporterLog, " Near: %f", c.znear);
+ qCDebug(GLTFExporterLog, " Far: %f", c.zfar);
}
}
}
@@ -952,110 +967,104 @@ void GLTFExporter::parseLights()
qCDebug(GLTFExporterLog, " Type: %i", lightInfo.type);
qCDebug(GLTFExporterLog, " Color: (%i, %i, %i, %i)", lightInfo.color.red(),
lightInfo.color.green(), lightInfo.color.blue(), lightInfo.color.alpha());
- qCDebug(GLTFExporterLog, " Intensity: %i", lightInfo.intensity);
+ qCDebug(GLTFExporterLog, " Intensity: %f", lightInfo.intensity);
qCDebug(GLTFExporterLog, " Direction: (%f, %f, %f)", lightInfo.direction.x(),
lightInfo.direction.y(), lightInfo.direction.z());
qCDebug(GLTFExporterLog, " Attenuation: (%f, %f, %f)", lightInfo.attenuation.x(),
lightInfo.attenuation.y(), lightInfo.attenuation.z());
- qCDebug(GLTFExporterLog, " CutOffAngle: %i", lightInfo.cutOffAngle);
+ qCDebug(GLTFExporterLog, " CutOffAngle: %f", lightInfo.cutOffAngle);
}
}
}
-static inline QJsonArray col2jsvec(const QColor &color, bool alpha = false)
+void GLTFExporter::parseTechniques(QMaterial *material)
{
- QJsonArray arr;
- arr << color.redF() << color.greenF() << color.blueF();
- if (alpha)
- arr << color.alphaF();
- return arr;
-}
+ int techniqueCount = 0;
+ qCDebug(GLTFExporterLog, " Parsing material techniques...");
-template <typename T>
-static inline QJsonArray vec2jsvec(const QVector<T> &v)
-{
- QJsonArray arr;
- for (int i = 0; i < v.count(); ++i)
- arr << v.at(i);
- return arr;
-}
+ for (auto technique : material->effect()->techniques()) {
+ QString techName;
+ if (m_techniqueIdMap.contains(technique)) {
+ techName = m_techniqueIdMap.value(technique);
+ } else {
+ techName = newTechniqueName();
+ parseRenderPasses(technique);
-static inline QJsonArray vec2jsvec(const QVector2D &v)
-{
- QJsonArray arr;
- arr << v.x() << v.y();
- return arr;
-}
+ }
+ m_techniqueIdMap.insert(technique, techName);
-static inline QJsonArray vec2jsvec(const QVector3D &v)
-{
- QJsonArray arr;
- arr << v.x() << v.y() << v.z();
- return arr;
-}
+ techniqueCount++;
-static inline QJsonArray vec2jsvec(const QVector4D &v)
-{
- QJsonArray arr;
- arr << v.x() << v.y() << v.z() << v.w();
- return arr;
+ if (GLTFExporterLog().isDebugEnabled()) {
+ qCDebug(GLTFExporterLog, " Technique #%i", techniqueCount);
+ qCDebug(GLTFExporterLog, " name: '%ls'", qUtf16PrintableImpl(techName));
+ }
+ }
}
-static inline QJsonArray matrix2jsvec(const QMatrix4x4 &matrix)
+void GLTFExporter::parseRenderPasses(QTechnique *technique)
{
- QJsonArray jm;
- const float *mtxp = matrix.constData();
- for (int j = 0; j < 16; ++j)
- jm.append(*mtxp++);
- return jm;
-}
+ int passCount = 0;
+ qCDebug(GLTFExporterLog, " Parsing render passes for technique...");
-static inline void setVarToJSonObject(QJsonObject &jsObj, const QString &key, const QVariant &var)
-{
- switch (var.type()) {
- case QVariant::Bool:
- jsObj[key] = var.toBool();
- break;
- case QMetaType::Float:
- jsObj[key] = var.value<float>();
- break;
- case QVariant::Vector2D:
- jsObj[key] = vec2jsvec(var.value<QVector2D>());
- break;
- case QVariant::Vector3D:
- jsObj[key] = vec2jsvec(var.value<QVector3D>());
- break;
- case QVariant::Vector4D:
- jsObj[key] = vec2jsvec(var.value<QVector4D>());
- break;
- case QVariant::Matrix4x4:
- jsObj[key] = matrix2jsvec(var.value<QMatrix4x4>());
- break;
- default:
- qCWarning(GLTFExporterLog, "Unknown value type for '%ls'", qUtf16PrintableImpl(key));
- break;
+ for (auto pass : technique->renderPasses()) {
+ QString name;
+ if (m_renderPassIdMap.contains(pass)) {
+ name = m_renderPassIdMap.value(pass);
+ } else {
+ name = newRenderPassName();
+ m_renderPassIdMap.insert(pass, name);
+ if (pass->shaderProgram() && !m_programInfo.contains(pass->shaderProgram())) {
+ ProgramInfo pi;
+ pi.name = newProgramName();
+ pi.vertexShader = addShaderInfo(QShaderProgram::Vertex,
+ pass->shaderProgram()->vertexShaderCode());
+ pi.tessellationControlShader =
+ addShaderInfo(QShaderProgram::Fragment,
+ pass->shaderProgram()->tessellationControlShaderCode());
+ pi.tessellationEvaluationShader =
+ addShaderInfo(QShaderProgram::TessellationControl,
+ pass->shaderProgram()->tessellationEvaluationShaderCode());
+ pi.geometryShader = addShaderInfo(QShaderProgram::TessellationEvaluation,
+ pass->shaderProgram()->geometryShaderCode());
+ pi.fragmentShader = addShaderInfo(QShaderProgram::Geometry,
+ pass->shaderProgram()->fragmentShaderCode());
+ pi.computeShader = addShaderInfo(QShaderProgram::Compute,
+ pass->shaderProgram()->computeShaderCode());
+ m_programInfo.insert(pass->shaderProgram(), pi);
+ qCDebug(GLTFExporterLog, " program: '%ls'", qUtf16PrintableImpl(pi.name));
+ }
+ }
+ passCount++;
+
+ if (GLTFExporterLog().isDebugEnabled()) {
+ qCDebug(GLTFExporterLog, " Render pass #%i", passCount);
+ qCDebug(GLTFExporterLog, " name: '%ls'", qUtf16PrintableImpl(name));
+ }
}
}
-static inline void promoteColorsToRGBA(QJsonObject *obj)
+QString GLTFExporter::addShaderInfo(QShaderProgram::ShaderType type, QByteArray code)
{
- auto it = obj->begin();
- auto itEnd = obj->end();
- while (it != itEnd) {
- QJsonArray arr = it.value().toArray();
- if (arr.count() == 3) {
- const QString key = it.key();
- if (key == QStringLiteral("ambient")
- || key == QStringLiteral("diffuse")
- || key == QStringLiteral("specular")
- || key == QStringLiteral("warm")
- || key == QStringLiteral("cool")) {
- arr.append(1);
- *it = arr;
- }
- }
- ++it;
+ if (code.isEmpty())
+ return QString();
+
+ for (auto si : m_shaderInfo) {
+ if (si.type == QShaderProgram::Vertex && code == si.code)
+ return si.name;
}
+
+ ShaderInfo newInfo;
+ newInfo.type = type;
+ newInfo.code = code;
+ newInfo.name = newShaderName();
+ newInfo.uri = newInfo.name + QStringLiteral(".glsl");
+
+ m_shaderInfo.append(newInfo);
+
+ qCDebug(GLTFExporterLog, " shader: '%ls'", qUtf16PrintableImpl(newInfo.name));
+
+ return newInfo.name;
}
bool GLTFExporter::saveScene()
@@ -1125,10 +1134,6 @@ bool GLTFExporter::saveScene()
accessor["count"] = int(acc.count);
accessor["componentType"] = int(acc.componentType);
accessor["type"] = acc.type;
- if (!acc.minVal.isEmpty() && !acc.maxVal.isEmpty()) {
- accessor["min"] = vec2jsvec(acc.minVal);
- accessor["max"] = vec2jsvec(acc.maxVal);
- }
accessors[acc.name] = accessor;
}
if (accessors.size())
@@ -1200,46 +1205,11 @@ bool GLTFExporter::saveScene()
m_obj["scene"] = QStringLiteral("defaultScene");
QJsonObject materials;
- QHash<QString, QString> textureNameMap;
- exportMaterials(materials, &textureNameMap);
+
+ exportMaterials(materials);
if (materials.size())
m_obj["materials"] = materials;
- QJsonObject textures;
- QHash<QString, QString> imageKeyMap; // uri -> key
- for (auto it = textureNameMap.constBegin(); it != textureNameMap.constEnd(); ++it) {
- QJsonObject texture;
- if (!imageKeyMap.contains(it.key()))
- imageKeyMap[it.key()] = newImageName();
- texture["source"] = imageKeyMap[it.key()];
- texture["format"] = 0x1908; // RGBA
- texture["internalFormat"] = 0x1908;
- texture["sampler"] = QStringLiteral("sampler_mip_rep");
- texture["target"] = 3553; // TEXTURE_2D
- texture["type"] = 5121; // UNSIGNED_BYTE
- textures[it.value()] = texture;
- }
- if (textures.size()) {
- m_obj["textures"] = textures;
- QJsonObject samplers;
- QJsonObject sampler;
- sampler["magFilter"] = 9729; // LINEAR
- sampler["minFilter"] = 9987; // LINEAR_MIPMAP_LINEAR
- sampler["wrapS"] = 10497; // REPEAT
- sampler["wrapT"] = 10497;
- samplers["sampler_mip_rep"] = sampler;
- m_obj["samplers"] = samplers;
- }
-
- QJsonObject images;
- for (auto it = imageKeyMap.constBegin(); it != imageKeyMap.constEnd(); ++it) {
- QJsonObject image;
- image["uri"] = m_imageMap.value(it.key());
- images[it.value()] = image;
- }
- if (images.size())
- m_obj["images"] = images;
-
// Lights must be declared as extensions to the top-level glTF object
QJsonObject lights;
for (auto lightInfo : m_lightInfo.values()) {
@@ -1283,7 +1253,192 @@ bool GLTFExporter::saveScene()
m_obj["extensions"] = extensions;
}
- // TODO: Save techniques, programs, and shaders for custom materials
+ // Save effects for custom materials
+ // Note that we are not saving effects, techniques, render passes, shader programs, or shaders
+ // strictly according to GLTF format, but rather in our expanded QGLTF custom format,
+ // since the GLTF format doesn't quite match our needs.
+ // Having our own format also vastly simplifies export and import of custom materials,
+ // since we are not trying to push a round peg into a square hole.
+ // If use cases arise in future where our exported GLTF scenes need to be loaded by third party
+ // GLTF loaders, we could add an export option to do so, but the exported scene would never
+ // be quite the same as the original.
+ QJsonObject effects;
+ for (auto it = m_effectIdMap.constBegin(); it != m_effectIdMap.constEnd(); ++it) {
+ QEffect *effect = it.key();
+ const QString effectName = it.value();
+ QJsonObject effectObj;
+ QJsonObject paramObj;
+
+ for (QParameter *param : effect->parameters())
+ exportParameter(paramObj, param->name(), param->value());
+ if (!effect->objectName().isEmpty())
+ effectObj["name"] = effect->objectName();
+ if (!paramObj.isEmpty())
+ effectObj["parameters"] = paramObj;
+ QJsonArray techs;
+ for (auto tech : effect->techniques())
+ techs << m_techniqueIdMap.value(tech);
+ effectObj["techniques"] = techs;
+ effects[effectName] = effectObj;
+ }
+ if (effects.size())
+ m_obj["effects"] = effects;
+
+ // Save techniques for custom materials.
+ QJsonObject techniques;
+ for (auto it = m_techniqueIdMap.constBegin(); it != m_techniqueIdMap.constEnd(); ++it) {
+ QTechnique *technique = it.key();
+
+ QJsonObject techObj;
+ QJsonObject filterKeyObj;
+ QJsonObject paramObj;
+ QJsonArray renderPassArr;
+
+ for (QFilterKey *filterKey : technique->filterKeys())
+ setVarToJSonObject(filterKeyObj, filterKey->name(), filterKey->value());
+
+ for (QRenderPass *pass : technique->renderPasses())
+ renderPassArr << m_renderPassIdMap.value(pass);
+
+ for (QParameter *param : technique->parameters())
+ exportParameter(paramObj, param->name(), param->value());
+
+ const QGraphicsApiFilter *gFilter = technique->graphicsApiFilter();
+ if (gFilter) {
+ QJsonObject graphicsApiFilterObj;
+ graphicsApiFilterObj["api"] = gFilter->api();
+ graphicsApiFilterObj["profile"] = gFilter->profile();
+ graphicsApiFilterObj["minorVersion"] = gFilter->minorVersion();
+ graphicsApiFilterObj["majorVersion"] = gFilter->majorVersion();
+ if (!gFilter->vendor().isEmpty())
+ graphicsApiFilterObj["vendor"] = gFilter->vendor();
+ QJsonArray extensions;
+ for (auto extName : gFilter->extensions())
+ extensions << extName;
+ if (!extensions.isEmpty())
+ graphicsApiFilterObj["extensions"] = extensions;
+ techObj["gapifilter"] = graphicsApiFilterObj;
+ }
+ if (!technique->objectName().isEmpty())
+ techObj["name"] = technique->objectName();
+ if (!filterKeyObj.isEmpty())
+ techObj["filterkeys"] = filterKeyObj;
+ if (!paramObj.isEmpty())
+ techObj["parameters"] = paramObj;
+ if (!renderPassArr.isEmpty())
+ techObj["renderpasses"] = renderPassArr;
+ techniques[it.value()] = techObj;
+ }
+ if (techniques.size())
+ m_obj["techniques"] = techniques;
+
+ // Save render passes for custom materials.
+ QJsonObject passes;
+ for (auto it = m_renderPassIdMap.constBegin(); it != m_renderPassIdMap.constEnd(); ++it) {
+ const QRenderPass *pass = it.key();
+ const QString passId = it.value();
+
+ QJsonObject passObj;
+ QJsonObject filterKeyObj;
+ QJsonObject paramObj;
+ QJsonObject stateObj;
+
+ for (QFilterKey *filterKey : pass->filterKeys())
+ setVarToJSonObject(filterKeyObj, filterKey->name(), filterKey->value());
+ for (QParameter *param : pass->parameters())
+ exportParameter(paramObj, param->name(), param->value());
+ exportRenderStates(stateObj, pass);
+
+ if (!pass->objectName().isEmpty())
+ passObj["name"] = pass->objectName();
+ if (!filterKeyObj.isEmpty())
+ passObj["filterkeys"] = filterKeyObj;
+ if (!paramObj.isEmpty())
+ passObj["parameters"] = paramObj;
+ if (!stateObj.isEmpty())
+ passObj["states"] = stateObj;
+ passObj["program"] = m_programInfo.value(pass->shaderProgram()).name;
+ passes[passId] = passObj;
+
+ }
+ if (passes.size())
+ m_obj["renderpasses"] = passes;
+
+ // Save programs for custom materials
+ QJsonObject programs;
+ for (auto it = m_programInfo.constBegin(); it != m_programInfo.constEnd(); ++it) {
+ const QShaderProgram *program = it.key();
+ const ProgramInfo pi = it.value();
+
+ QJsonObject progObj;
+ if (!program->objectName().isEmpty())
+ progObj["name"] = program->objectName();
+ progObj["vertexShader"] = pi.vertexShader;
+ progObj["fragmentShader"] = pi.fragmentShader;
+ // Qt3D additions
+ if (!pi.tessellationControlShader.isEmpty())
+ progObj["tessCtrlShader"] = pi.tessellationControlShader;
+ if (!pi.tessellationEvaluationShader.isEmpty())
+ progObj["tessEvalShader"] = pi.tessellationEvaluationShader;
+ if (!pi.geometryShader.isEmpty())
+ progObj["geometryShader"] = pi.geometryShader;
+ if (!pi.computeShader.isEmpty())
+ progObj["computeShader"] = pi.computeShader;
+ programs[pi.name] = progObj;
+
+ }
+ if (programs.size())
+ m_obj["programs"] = programs;
+
+ // Save shaders for custom materials
+ QJsonObject shaders;
+ for (auto si : m_shaderInfo) {
+ QJsonObject shaderObj;
+ shaderObj["uri"] = si.uri;
+ shaders[si.name] = shaderObj;
+
+ }
+ if (shaders.size())
+ m_obj["shaders"] = shaders;
+
+ // Copy textures and shaders into temporary directory
+ copyTextures();
+ createShaders();
+
+ QJsonObject textures;
+ QHash<QString, QString> imageKeyMap; // uri -> key
+ for (auto it = m_textureIdMap.constBegin(); it != m_textureIdMap.constEnd(); ++it) {
+ QJsonObject texture;
+ if (!imageKeyMap.contains(it.key()))
+ imageKeyMap[it.key()] = newImageName();
+ texture["source"] = imageKeyMap[it.key()];
+ texture["format"] = GL_RGBA;
+ texture["internalFormat"] = GL_RGBA;
+ texture["sampler"] = QStringLiteral("sampler_mip_rep");
+ texture["target"] = GL_TEXTURE_2D;
+ texture["type"] = GL_UNSIGNED_BYTE;
+ textures[it.value()] = texture;
+ }
+ if (textures.size()) {
+ m_obj["textures"] = textures;
+ QJsonObject samplers;
+ QJsonObject sampler;
+ sampler["magFilter"] = GL_LINEAR;
+ sampler["minFilter"] = GL_LINEAR_MIPMAP_LINEAR;
+ sampler["wrapS"] = GL_REPEAT;
+ sampler["wrapT"] = GL_REPEAT;
+ samplers["sampler_mip_rep"] = sampler;
+ m_obj["samplers"] = samplers;
+ }
+
+ QJsonObject images;
+ for (auto it = imageKeyMap.constBegin(); it != imageKeyMap.constEnd(); ++it) {
+ QJsonObject image;
+ image["uri"] = m_imageMap.value(it.key());
+ images[it.value()] = image;
+ }
+ if (images.size())
+ m_obj["images"] = images;
m_doc.setObject(m_obj);
@@ -1383,70 +1538,75 @@ QString GLTFExporter::exportNodes(GLTFExporter::Node *n, QJsonObject &nodes)
return n->uniqueName;
}
-void GLTFExporter::exportMaterials(QJsonObject &materials, QHash<QString, QString> *textureNameMap)
+void GLTFExporter::exportMaterials(QJsonObject &materials)
{
QHash<QString, bool> imageHasAlpha;
- QHashIterator<Qt3DRender::QMaterial *, MaterialInfo> matIt(m_materialInfo);
+ QHashIterator<QMaterial *, MaterialInfo> matIt(m_materialInfo);
for (auto matIt = m_materialInfo.constBegin(); matIt != m_materialInfo.constEnd(); ++matIt) {
+ const QMaterial *material = matIt.key();
const MaterialInfo &matInfo = matIt.value();
- QJsonObject material;
- material["name"] = matInfo.originalName;
-
- bool opaque = true;
- QJsonObject vals;
- for (auto it = matInfo.textures.constBegin(); it != matInfo.textures.constEnd(); ++it) {
- if (!textureNameMap->contains(it.value()))
- textureNameMap->insert(it.value(), newTextureName());
- QString key = it.key();
- if (key == QStringLiteral("normal")) // avoid clashing with the vertex normals
- key = QStringLiteral("normalmap");
- // Alpha is supported for diffuse textures, but have to check the image data to decide
- // if blending is needed
- if (key == QStringLiteral("diffuse")) {
- QString imgFn = it.value();
- if (imageHasAlpha.contains(imgFn)) {
- if (imageHasAlpha[imgFn])
- opaque = false;
- } else {
- QImage img(imgFn);
- if (!img.isNull()) {
- if (img.hasAlphaChannel()) {
- for (int y = 0; opaque && y < img.height(); ++y) {
- for (int x = 0; opaque && x < img.width(); ++x) {
- if (qAlpha(img.pixel(x, y)) < 255)
- opaque = false;
+ QJsonObject materialObj;
+ materialObj["name"] = matInfo.originalName;
+
+ if (matInfo.type == MaterialInfo::TypeCustom) {
+ QVector<QParameter *> parameters = material->parameters();
+ QJsonObject paramObj;
+ for (auto param : parameters)
+ exportParameter(paramObj, param->name(), param->value());
+ materialObj["effect"] = m_effectIdMap.value(material->effect());
+ materialObj["parameters"] = paramObj;
+ } else {
+ bool opaque = true;
+ QJsonObject vals;
+ for (auto it = matInfo.textures.constBegin(); it != matInfo.textures.constEnd(); ++it) {
+ QString key = it.key();
+ if (key == QStringLiteral("normal")) // avoid clashing with the vertex normals
+ key = QStringLiteral("normalmap");
+ // Alpha is supported for diffuse textures, but have to check the image data to
+ // decide if blending is needed
+ if (key == QStringLiteral("diffuse")) {
+ QString imgFn = it.value();
+ if (imageHasAlpha.contains(imgFn)) {
+ if (imageHasAlpha[imgFn])
+ opaque = false;
+ } else {
+ QImage img(imgFn);
+ if (!img.isNull()) {
+ if (img.hasAlphaChannel()) {
+ for (int y = 0; opaque && y < img.height(); ++y) {
+ for (int x = 0; opaque && x < img.width(); ++x) {
+ if (qAlpha(img.pixel(x, y)) < 255)
+ opaque = false;
+ }
}
}
+ imageHasAlpha[imgFn] = !opaque;
+ } else {
+ qCWarning(GLTFExporterLog,
+ "Cannot determine presence of alpha for '%ls'",
+ qUtf16PrintableImpl(imgFn));
}
- imageHasAlpha[imgFn] = !opaque;
- } else {
- qCWarning(GLTFExporterLog, "Cannot determine presence of alpha for '%ls'",
- qUtf16PrintableImpl(imgFn));
}
}
+ vals[key] = m_textureIdMap.value(it.value());
+ }
+ for (auto it = matInfo.values.constBegin(); it != matInfo.values.constEnd(); ++it) {
+ if (vals.contains(it.key()))
+ continue;
+ setVarToJSonObject(vals, it.key(), it.value());
+ }
+ for (auto it = matInfo.colors.constBegin(); it != matInfo.colors.constEnd(); ++it) {
+ if (vals.contains(it.key()))
+ continue;
+ // Alpha is supported for the diffuse color. < 1 will enable blending.
+ const bool alpha = (it.key() == QStringLiteral("diffuse"))
+ && (matInfo.type != MaterialInfo::TypeCustom);
+ if (alpha && it.value().alphaF() < 1.0f)
+ opaque = false;
+ vals[it.key()] = col2jsvec(it.value(), alpha);
}
- vals[key] = textureNameMap->value(it.value());
- }
- for (auto it = matInfo.values.constBegin(); it != matInfo.values.constEnd(); ++it) {
- if (vals.contains(it.key()))
- continue;
- setVarToJSonObject(vals, it.key(), it.value());
- }
- for (auto it = matInfo.colors.constBegin(); it != matInfo.colors.constEnd(); ++it) {
- if (vals.contains(it.key()))
- continue;
- // Alpha is supported for the diffuse color. < 1 will enable blending.
- const bool alpha = it.key() == QStringLiteral("diffuse");
- if (alpha && it.value().alphaF() < 1.0f)
- opaque = false;
- vals[it.key()] = col2jsvec(it.value(), alpha);
- }
- if (matInfo.type == MaterialInfo::TypeCustom) {
- material["values"] = vals;
- // TODO: custom materials
- } else {
// Material is a common material, so export it as such.
QJsonObject commonMat;
if (matInfo.type == MaterialInfo::TypeGooch)
@@ -1473,10 +1633,10 @@ void GLTFExporter::exportMaterials(QJsonObject &materials, QHash<QString, QStrin
commonMat["functions"] = functions;
QJsonObject extensions;
extensions["KHR_materials_common"] = commonMat;
- material["extensions"] = extensions;
+ materialObj["extensions"] = extensions;
}
- materials[matInfo.name] = material;
+ materials[matInfo.name] = materialObj;
}
}
@@ -1504,6 +1664,197 @@ void GLTFExporter::clearOldExport(const QString &dir)
}
}
+void GLTFExporter::exportParameter(QJsonObject &jsonObj, const QString &name,
+ const QVariant &variant)
+{
+ QLatin1String typeStr("type");
+ QLatin1String valueStr("value");
+
+ QJsonObject paramObj;
+
+ if (variant.canConvert<QAbstractTexture *>()) {
+ paramObj[typeStr] = GL_SAMPLER_2D;
+ paramObj[valueStr] = m_textureIdMap.value(textureVariantToUrl(variant));
+ } else {
+ switch (QMetaType::Type(variant.type())) {
+ case QMetaType::Bool:
+ paramObj[typeStr] = GL_BOOL;
+ paramObj[valueStr] = variant.toBool();
+ break;
+ case QMetaType::Int: // fall through
+ case QMetaType::Long: // fall through
+ case QMetaType::LongLong:
+ paramObj[typeStr] = GL_INT;
+ paramObj[valueStr] = variant.toInt();
+ break;
+ case QMetaType::UInt: // fall through
+ case QMetaType::ULong: // fall through
+ case QMetaType::ULongLong:
+ paramObj[typeStr] = GL_UNSIGNED_INT;
+ paramObj[valueStr] = variant.toInt();
+ break;
+ case QMetaType::Short:
+ paramObj[typeStr] = GL_SHORT;
+ paramObj[valueStr] = variant.toInt();
+ break;
+ case QMetaType::UShort:
+ paramObj[typeStr] = GL_UNSIGNED_SHORT;
+ paramObj[valueStr] = variant.toInt();
+ break;
+ case QMetaType::Char:
+ paramObj[typeStr] = GL_BYTE;
+ paramObj[valueStr] = variant.toInt();
+ break;
+ case QMetaType::UChar:
+ paramObj[typeStr] = GL_UNSIGNED_BYTE;
+ paramObj[valueStr] = variant.toInt();
+ break;
+ case QMetaType::QColor:
+ paramObj[typeStr] = GL_FLOAT_VEC4;
+ paramObj[valueStr] = col2jsvec(variant.value<QColor>(), true);
+ break;
+ case QMetaType::Float:
+ paramObj[typeStr] = GL_FLOAT;
+ paramObj[valueStr] = variant.value<float>();
+ break;
+ case QMetaType::QVector2D:
+ paramObj[typeStr] = GL_FLOAT_VEC2;
+ paramObj[valueStr] = vec2jsvec(variant.value<QVector2D>());
+ break;
+ case QMetaType::QVector3D:
+ paramObj[typeStr] = GL_FLOAT_VEC3;
+ paramObj[valueStr] = vec2jsvec(variant.value<QVector3D>());
+ break;
+ case QMetaType::QVector4D:
+ paramObj[typeStr] = GL_FLOAT_VEC4;
+ paramObj[valueStr] = vec2jsvec(variant.value<QVector4D>());
+ break;
+ case QMetaType::QMatrix4x4:
+ paramObj[typeStr] = GL_FLOAT_MAT4;
+ paramObj[valueStr] = matrix2jsvec(variant.value<QMatrix4x4>());
+ break;
+ default:
+ qCWarning(GLTFExporterLog, "Unknown value type for '%ls'", qUtf16PrintableImpl(name));
+ break;
+ }
+ }
+
+ jsonObj[name] = paramObj;
+}
+
+void GLTFExporter::exportRenderStates(QJsonObject &jsonObj, const QRenderPass *pass)
+{
+ QJsonArray enableStates;
+ QJsonObject funcObj;
+ for (QRenderState *state : pass->renderStates()) {
+ QJsonArray arr;
+ if (qobject_cast<QAlphaCoverage *>(state)) {
+ enableStates << GL_SAMPLE_ALPHA_TO_COVERAGE;
+ } else if (qobject_cast<QAlphaTest *>(state)) {
+ auto s = qobject_cast<QAlphaTest *>(state);
+ arr << s->alphaFunction();
+ arr << s->referenceValue();
+ funcObj["alphaTest"] = arr;
+ } else if (qobject_cast<QBlendEquation *>(state)) {
+ auto s = qobject_cast<QBlendEquation *>(state);
+ arr << s->blendFunction();
+ funcObj["blendEquationSeparate"] = arr;
+ } else if (qobject_cast<QBlendEquationArguments *>(state)) {
+ auto s = qobject_cast<QBlendEquationArguments *>(state);
+ arr << s->sourceRgb();
+ arr << s->sourceAlpha();
+ arr << s->destinationRgb();
+ arr << s->destinationAlpha();
+ arr << s->bufferIndex();
+ funcObj["blendFuncSeparate"] = arr;
+ } else if (qobject_cast<QClipPlane *>(state)) {
+ auto s = qobject_cast<QClipPlane *>(state);
+ arr << s->planeIndex();
+ arr << s->normal().x();
+ arr << s->normal().y();
+ arr << s->normal().z();
+ arr << s->distance();
+ funcObj["clipPlane"] = arr;
+ } else if (qobject_cast<QColorMask *>(state)) {
+ auto s = qobject_cast<QColorMask *>(state);
+ arr << s->isRedMasked();
+ arr << s->isGreenMasked();
+ arr << s->isBlueMasked();
+ arr << s->isAlphaMasked();
+ funcObj["colorMask"] = arr;
+ } else if (qobject_cast<QCullFace *>(state)) {
+ auto s = qobject_cast<QCullFace *>(state);
+ arr << s->mode();
+ funcObj["cullFace"] = arr;
+ } else if (qobject_cast<QDepthTest *>(state)) {
+ auto s = qobject_cast<QDepthTest *>(state);
+ arr << s->depthFunction();
+ funcObj["depthFunc"] = arr;
+ } else if (qobject_cast<QDithering *>(state)) {
+ enableStates << GL_DITHER;
+ } else if (qobject_cast<QFrontFace *>(state)) {
+ auto s = qobject_cast<QFrontFace *>(state);
+ arr << s->direction();
+ funcObj["frontFace"] = arr;
+ } else if (qobject_cast<QFrontFace *>(state)) {
+ auto s = qobject_cast<QFrontFace *>(state);
+ arr << s->direction();
+ funcObj["frontFace"] = arr;
+ } else if (qobject_cast<QMultiSampleAntiAliasing *>(state)) {
+ enableStates << 0x809D; // GL_MULTISAMPLE
+ } else if (qobject_cast<QNoDepthMask *>(state)) {
+ arr << false;
+ funcObj["depthMask"] = arr;
+ } else if (qobject_cast<QPointSize *>(state)) {
+ auto s = qobject_cast<QPointSize *>(state);
+ arr << s->sizeMode();
+ arr << s->value();
+ funcObj["pointSize"] = arr;
+ } else if (qobject_cast<QPolygonOffset *>(state)) {
+ auto s = qobject_cast<QPolygonOffset *>(state);
+ arr << s->scaleFactor();
+ arr << s->depthSteps();
+ funcObj["polygonOffset"] = arr;
+ } else if (qobject_cast<QScissorTest *>(state)) {
+ auto s = qobject_cast<QScissorTest *>(state);
+ arr << s->left();
+ arr << s->bottom();
+ arr << s->width();
+ arr << s->height();
+ funcObj["scissor"] = arr;
+ } else if (qobject_cast<QSeamlessCubemap *>(state)) {
+ enableStates << 0x884F; // GL_TEXTURE_CUBE_MAP_SEAMLESS
+ } else if (qobject_cast<QStencilMask *>(state)) {
+ auto s = qobject_cast<QStencilMask *>(state);
+ arr << int(s->frontOutputMask());
+ arr << int(s->backOutputMask());
+ funcObj["stencilMask"] = arr;
+ } else if (qobject_cast<QStencilOperation *>(state)) {
+ auto s = qobject_cast<QStencilOperation *>(state);
+ arr << s->front()->stencilTestFailureOperation();
+ arr << s->front()->depthTestFailureOperation();
+ arr << s->front()->allTestsPassOperation();
+ arr << s->back()->stencilTestFailureOperation();
+ arr << s->back()->depthTestFailureOperation();
+ arr << s->back()->allTestsPassOperation();
+ funcObj["stencilOperation"] = arr;
+ } else if (qobject_cast<QStencilTest *>(state)) {
+ auto s = qobject_cast<QStencilTest *>(state);
+ arr << int(s->front()->comparisonMask());
+ arr << s->front()->referenceValue();
+ arr << s->front()->stencilFunction();
+ arr << int(s->back()->comparisonMask());
+ arr << s->back()->referenceValue();
+ arr << s->back()->stencilFunction();
+ funcObj["stencilTest"] = arr;
+ }
+ }
+ if (!enableStates.isEmpty())
+ jsonObj["enable"] = enableStates;
+ if (!funcObj.isEmpty())
+ jsonObj["functions"] = funcObj;
+}
+
QString GLTFExporter::newBufferViewName()
{
return QString(QStringLiteral("bufferView_%1")).arg(++m_bufferViewCount);
@@ -1564,6 +1915,64 @@ QString GLTFExporter::newLightName()
return QString(QStringLiteral("light_%1")).arg(++m_lightCount);
}
+QString GLTFExporter::newRenderPassName()
+{
+ return QString(QStringLiteral("renderpass_%1")).arg(++m_renderPassCount);
+}
+
+QString GLTFExporter::newEffectName()
+{
+ return QString(QStringLiteral("effect_%1")).arg(++m_effectCount);
+}
+
+QString GLTFExporter::textureVariantToUrl(const QVariant &var)
+{
+ QString urlString;
+ QAbstractTexture *texture = var.value<QAbstractTexture *>();
+ if (texture->textureImages().size()) {
+ QTextureImage *image = qobject_cast<QTextureImage *>(texture->textureImages().at(0));
+ if (image) {
+ urlString = QUrlHelper::urlToLocalFileOrQrc(image->source());
+ if (!m_textureIdMap.contains(urlString))
+ m_textureIdMap.insert(urlString, newTextureName());
+ }
+ }
+ return urlString;
+}
+
+void GLTFExporter::setVarToJSonObject(QJsonObject &jsObj, const QString &key, const QVariant &var)
+{
+ switch (QMetaType::Type(var.type())) {
+ case QMetaType::Bool:
+ jsObj[key] = var.toBool();
+ break;
+ case QMetaType::Float:
+ jsObj[key] = var.value<float>();
+ break;
+ case QMetaType::QVector2D:
+ jsObj[key] = vec2jsvec(var.value<QVector2D>());
+ break;
+ case QMetaType::QVector3D:
+ jsObj[key] = vec2jsvec(var.value<QVector3D>());
+ break;
+ case QMetaType::QVector4D:
+ jsObj[key] = vec2jsvec(var.value<QVector4D>());
+ break;
+ case QMetaType::QMatrix4x4:
+ jsObj[key] = matrix2jsvec(var.value<QMatrix4x4>());
+ break;
+ case QMetaType::QString:
+ jsObj[key] = var.toString();
+ break;
+ case QMetaType::QColor:
+ jsObj[key] = col2jsvec(var.value<QColor>(), true);
+ break;
+ default:
+ qCWarning(GLTFExporterLog, "Unknown value type for '%ls'", qUtf16PrintableImpl(key));
+ break;
+ }
+}
+
} // namespace Qt3DRender
QT_END_NAMESPACE
diff --git a/src/plugins/sceneparsers/gltfexport/gltfexporter.h b/src/plugins/sceneparsers/gltfexport/gltfexporter.h
index b96788db7..5dd2e337b 100644
--- a/src/plugins/sceneparsers/gltfexport/gltfexporter.h
+++ b/src/plugins/sceneparsers/gltfexport/gltfexporter.h
@@ -57,6 +57,7 @@
#include <QtGui/qvector3d.h>
#include <Qt3DRender/qabstractlight.h>
+#include <Qt3DRender/qshaderprogram.h>
#include <private/qsceneexporter_p.h>
@@ -74,6 +75,9 @@ namespace Qt3DRender {
class QCameraLens;
class QMaterial;
class QGeometryRenderer;
+class QTechnique;
+class QRenderPass;
+class QEffect;
Q_DECLARE_LOGGING_CATEGORY(GLTFExporterLog)
@@ -115,8 +119,6 @@ private:
uint count;
uint componentType;
QString type;
- QVector<float> minVal;
- QVector<float> maxVal;
};
QVector<Accessor> accessors;
QString name; // generated
@@ -141,14 +143,32 @@ private:
QString name;
QString originalName;
MaterialType type;
+
+ // These are only used for default materials
QHash<QString, QColor> colors;
QHash<QString, QString> textures;
QHash<QString, QVariant> values;
- QHash<QString, QString> techniques;
QVector<int> blendArguments;
QVector<int> blendEquations;
};
+ struct ProgramInfo {
+ QString name;
+ QString vertexShader;
+ QString tessellationControlShader;
+ QString tessellationEvaluationShader;
+ QString geometryShader;
+ QString fragmentShader;
+ QString computeShader;
+ };
+
+ struct ShaderInfo {
+ QString name;
+ QString uri;
+ QShaderProgram::ShaderType type;
+ QByteArray code;
+ };
+
struct CameraInfo {
QString name;
QString originalName;
@@ -183,18 +203,24 @@ private:
};
void copyTextures();
+ void createShaders();
void parseEntities(const Qt3DCore::QEntity *entity, Node *parentNode);
void parseScene();
void parseMaterials();
void parseMeshes();
void parseCameras();
void parseLights();
+ void parseTechniques(QMaterial *material);
+ void parseRenderPasses(QTechnique *technique);
+ QString addShaderInfo(QShaderProgram::ShaderType type, QByteArray code);
bool saveScene();
void delNode(Node *n);
QString exportNodes(Node *n, QJsonObject &nodes);
- void exportMaterials(QJsonObject &materials, QHash<QString, QString> *textureNameMap);
+ void exportMaterials(QJsonObject &materials);
void clearOldExport(const QString &dir);
+ void exportParameter(QJsonObject &jsonObj, const QString &name, const QVariant &variant);
+ void exportRenderStates(QJsonObject &jsonObj, const QRenderPass *pass);
QString newBufferViewName();
QString newAccessorName();
@@ -208,6 +234,11 @@ private:
QString newNodeName();
QString newCameraName();
QString newLightName();
+ QString newRenderPassName();
+ QString newEffectName();
+
+ QString textureVariantToUrl(const QVariant &var);
+ void setVarToJSonObject(QJsonObject &jsObj, const QString &key, const QVariant &var);
int m_bufferViewCount;
int m_accessorCount;
@@ -221,6 +252,8 @@ private:
int m_nodeCount;
int m_cameraCount;
int m_lightCount;
+ int m_renderPassCount;
+ int m_effectCount;
Qt3DCore::QEntity *m_sceneRoot;
QString m_exportName;
@@ -238,11 +271,18 @@ private:
QHash<Node *, Qt3DRender::QAbstractLight *> m_lightMap;
QHash<Node *, Qt3DCore::QTransform *> m_transformMap;
QHash<QString, QString> m_imageMap; // Original texture URL -> generated filename
+ QHash<QString, QString> m_textureIdMap;
+ QHash<Qt3DRender::QRenderPass *, QString> m_renderPassIdMap;
+ QHash<Qt3DRender::QEffect *, QString> m_effectIdMap;
+ QHash<Qt3DRender::QTechnique *, QString> m_techniqueIdMap;
QHash<Qt3DRender::QGeometryRenderer *, MeshInfo> m_meshInfo;
QHash<Qt3DRender::QMaterial *, MaterialInfo> m_materialInfo;
QHash<Qt3DRender::QCameraLens *, CameraInfo> m_cameraInfo;
QHash<Qt3DRender::QAbstractLight *, LightInfo> m_lightInfo;
+ QHash<Qt3DRender::QShaderProgram *, ProgramInfo> m_programInfo;
+ QVector<ShaderInfo> m_shaderInfo;
+
Node *m_rootNode;
bool m_rootNodeEmpty;
diff --git a/tests/auto/render/gltfplugins/images.qrc b/tests/auto/render/gltfplugins/images.qrc
index 3938bbe18..04d711819 100644
--- a/tests/auto/render/gltfplugins/images.qrc
+++ b/tests/auto/render/gltfplugins/images.qrc
@@ -4,5 +4,9 @@
<file>qtlogo_normal.png</file>
<file>qtlogo_specular.png</file>
<file>qtlogo_with_alpha.png</file>
+ <file>ontopmaterial.frag</file>
+ <file>ontopmaterial.vert</file>
+ <file>ontopmaterialES2.frag</file>
+ <file>ontopmaterialES2.vert</file>
</qresource>
</RCC>
diff --git a/tests/auto/render/gltfplugins/ontopmaterial.frag b/tests/auto/render/gltfplugins/ontopmaterial.frag
new file mode 100644
index 000000000..2434f5ac8
--- /dev/null
+++ b/tests/auto/render/gltfplugins/ontopmaterial.frag
@@ -0,0 +1,14 @@
+#version 150 core
+
+uniform vec4 handleColor;
+uniform float customAlpha;
+
+uniform sampler2D customTexture;
+
+out vec4 fragColor;
+
+void main()
+{
+ vec3 customColor = texture(customTexture, vec2(0.4, 0.4)).rgb;
+ fragColor = vec4(handleColor.xy, customColor.z, customAlpha);
+}
diff --git a/tests/auto/render/gltfplugins/ontopmaterial.vert b/tests/auto/render/gltfplugins/ontopmaterial.vert
new file mode 100644
index 000000000..7cb0c5ab1
--- /dev/null
+++ b/tests/auto/render/gltfplugins/ontopmaterial.vert
@@ -0,0 +1,20 @@
+#version 150 core
+
+in vec3 vertexPosition;
+in vec3 vertexOffset;
+
+uniform mat4 modelViewProjection;
+uniform vec3 globalOffset;
+uniform int extraYOffset;
+uniform bool reverseOffset;
+
+void main()
+{
+ vec4 offset = vec4(globalOffset, 0.0) + vec4(0.0f, float(extraYOffset), 0.0f, 0.0f);
+ if (reverseOffset)
+ offset *= -1.0f;
+ gl_Position = modelViewProjection
+ * (vec4(vertexPosition, 1.0) + offset + vec4(vertexOffset, 0.0));
+ // Set the Z value of the vertex so that it'll always get drawn on top of everything else
+ gl_Position.z = -1.0;
+}
diff --git a/tests/auto/render/gltfplugins/ontopmaterialES2.frag b/tests/auto/render/gltfplugins/ontopmaterialES2.frag
new file mode 100644
index 000000000..a40f7e810
--- /dev/null
+++ b/tests/auto/render/gltfplugins/ontopmaterialES2.frag
@@ -0,0 +1,10 @@
+uniform lowp vec4 handleColor;
+uniform lowp float customAlpha;
+
+uniform sampler2D customTexture;
+
+void main()
+{
+ vec3 customColor = texture(customTexture, vec2(0.4, 0.4)).rgb;
+ gl_FragColor = vec4(handleColor.xy, customColor.z, customAlpha);
+}
diff --git a/tests/auto/render/gltfplugins/ontopmaterialES2.vert b/tests/auto/render/gltfplugins/ontopmaterialES2.vert
new file mode 100644
index 000000000..41add49a5
--- /dev/null
+++ b/tests/auto/render/gltfplugins/ontopmaterialES2.vert
@@ -0,0 +1,18 @@
+attribute highp vec3 vertexPosition;
+attribute highp vec3 vertexOffset;
+
+uniform highp mat4 modelViewProjection;
+uniform highp vec3 globalOffset;
+uniform int extraYOffset;
+uniform bool reverseOffset;
+
+void main()
+{
+ vec4 offset = vec4(globalOffset, 0.0) + vec4(0.0f, float(extraYOffset), 0.0f, 0.0f);
+ if (reverseOffset)
+ offset *= -1.0f;
+ gl_Position = modelViewProjection
+ * (vec4(vertexPosition, 1.0) + offset + vec4(vertexOffset, 0.0));
+ // Set the Z value of the vertex so that it'll always get drawn on top of everything else
+ gl_Position.z = -1.0;
+}
diff --git a/tests/auto/render/gltfplugins/tst_gltfplugins.cpp b/tests/auto/render/gltfplugins/tst_gltfplugins.cpp
index 2e0db3d73..1a49771d8 100644
--- a/tests/auto/render/gltfplugins/tst_gltfplugins.cpp
+++ b/tests/auto/render/gltfplugins/tst_gltfplugins.cpp
@@ -59,6 +59,14 @@
#include <Qt3DRender/qattribute.h>
#include <Qt3DRender/qbuffer.h>
#include <Qt3DRender/qeffect.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/qfilterkey.h>
+#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qcolormask.h>
+#include <Qt3DRender/qblendequation.h>
#include <Qt3DExtras/qconemesh.h>
#include <Qt3DExtras/qcuboidmesh.h>
@@ -120,6 +128,15 @@ private:
Qt3DRender::QAttribute::AttributeType type,
Qt3DRender::QGeometry *geometry);
void compareAttributes(Qt3DRender::QAttribute *a1, Qt3DRender::QAttribute *a2);
+ void compareParameters(const QVector<Qt3DRender::QParameter *> &params1,
+ const QVector<Qt3DRender::QParameter *> &params2);
+ void compareRenderPasses(const QVector<Qt3DRender::QRenderPass *> &passes1,
+ const QVector<Qt3DRender::QRenderPass *> &passes2);
+ void compareFilterKeys(const QVector<Qt3DRender::QFilterKey *> &keys1,
+ const QVector<Qt3DRender::QFilterKey *> &keys2);
+ QUrl getTextureUrl(Qt3DRender::QAbstractTexture *tex);
+ Qt3DRender::QGeometryRenderer *createCustomCube();
+ Qt3DRender::QEffect *createOnTopEffect();
QTemporaryDir *m_exportDir;
Qt3DExtras::Qt3DWindow *m_view1;
@@ -493,70 +510,23 @@ void tst_gltfPlugins::createTestScene()
transform->setTranslation(QVector3D(4.0f, 3.0f, -15.0f));
transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(1.0f, 1.0f, 1.0f, 270.0f));
- Qt3DRender::QGeometryRenderer *boxMesh = new Qt3DRender::QGeometryRenderer();
- Qt3DRender::QGeometry *boxGeometry = new Qt3DRender::QGeometry(boxMesh);
- Qt3DRender::QBuffer *boxDataBuffer =
- new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, boxGeometry);
+ Qt3DRender::QGeometryRenderer *boxMesh = createCustomCube();
Qt3DRender::QBuffer *colorDataBuffer =
- new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, boxGeometry);
- Qt3DRender::QBuffer *indexDataBuffer =
- new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, boxGeometry);
- QByteArray vertexBufferData;
+ new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, boxMesh->geometry());
QByteArray colorBufferData;
- QByteArray indexBufferData;
-
- vertexBufferData.resize(8 * 3 * sizeof(float));
colorBufferData.resize(8 * 4 * sizeof(float));
- indexBufferData.resize(12 * 3 * sizeof(ushort));
-
- float dimension = 1.0f;
-
- float *vPtr = reinterpret_cast<float *>(vertexBufferData.data());
- vPtr[0] = -dimension; vPtr[1] = -dimension; vPtr[2] = -dimension;
- vPtr[3] = dimension; vPtr[4] = -dimension; vPtr[5] = -dimension;
- vPtr[6] = dimension; vPtr[7] = -dimension; vPtr[8] = dimension;
- vPtr[9] = -dimension; vPtr[10] = -dimension; vPtr[11] = dimension;
- vPtr[12] = -dimension; vPtr[13] = dimension; vPtr[14] = -dimension;
- vPtr[15] = dimension; vPtr[16] = dimension; vPtr[17] = -dimension;
- vPtr[18] = dimension; vPtr[19] = dimension; vPtr[20] = dimension;
- vPtr[21] = -dimension; vPtr[22] = dimension; vPtr[23] = dimension;
float *cPtr = reinterpret_cast<float *>(colorBufferData.data());
for (int i = 0; i < 8; i++) {
- cPtr[i * 4] = vPtr[i * 3];
- cPtr[i * 4 + 1] = vPtr[i * 3 + 1];
- cPtr[i * 4 + 2] = vPtr[i * 3 + 2];
+ cPtr[i * 4] = float(i) / 8.0f;
+ cPtr[i * 4 + 1] = float(8 - i) / 8.0f;
+ cPtr[i * 4 + 2] = float((i + 4) % 8) / 8.0f;
cPtr[i * 4 + 3] = 1.0f;
}
- ushort *iPtr = reinterpret_cast<ushort *>(indexBufferData.data());
- iPtr[0] = 2; iPtr[1] = 0; iPtr[2] = 1;
- iPtr[3] = 2; iPtr[4] = 3; iPtr[5] = 0;
- iPtr[6] = 1; iPtr[7] = 6; iPtr[8] = 2;
- iPtr[9] = 1; iPtr[10] = 5; iPtr[11] = 6;
- iPtr[12] = 2; iPtr[13] = 7; iPtr[14] = 3;
- iPtr[15] = 2; iPtr[16] = 6; iPtr[17] = 7;
- iPtr[18] = 6; iPtr[19] = 5; iPtr[20] = 4;
- iPtr[21] = 6; iPtr[22] = 4; iPtr[23] = 7;
- iPtr[24] = 7; iPtr[25] = 0; iPtr[26] = 3;
- iPtr[27] = 7; iPtr[28] = 4; iPtr[29] = 0;
- iPtr[30] = 4; iPtr[31] = 1; iPtr[32] = 0;
- iPtr[33] = 4; iPtr[34] = 5; iPtr[35] = 1;
-
- boxDataBuffer->setData(vertexBufferData);
colorDataBuffer->setData(colorBufferData);
- indexDataBuffer->setData(indexBufferData);
-
- addPositionAttributeToGeometry(boxGeometry, boxDataBuffer, 8);
- addColorAttributeToGeometry(boxGeometry, colorDataBuffer, 8);
- addIndexAttributeToGeometry(boxGeometry, indexDataBuffer, 36);
- boxMesh->setInstanceCount(1);
- boxMesh->setIndexOffset(0);
- boxMesh->setFirstInstance(0);
- boxMesh->setVertexCount(36);
- boxMesh->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
- boxMesh->setGeometry(boxGeometry);
+ addColorAttributeToGeometry(boxMesh->geometry(), colorDataBuffer, 8);
createAndAddEntity(QStringLiteral("Custom cube with per-vertex colors"),
boxMesh, material, transform);
@@ -580,6 +550,52 @@ void tst_gltfPlugins::createTestScene()
createAndAddEntity(QStringLiteral("Child with Phong"),
mesh, material, transform, parentEntity);
}
+ // Cube with custom material
+ {
+ Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial;
+ material->setEffect(createOnTopEffect());
+ material->addParameter(new Qt3DRender::QParameter(QStringLiteral("globalOffset"),
+ QVector3D(-3.0f, 0.0f, 3.0f)));
+ material->addParameter(new Qt3DRender::QParameter(QStringLiteral("extraYOffset"), 3));
+ material->effect()->addParameter(new Qt3DRender::QParameter(QStringLiteral("handleColor"),
+ QColor(Qt::magenta)));
+ material->effect()->addParameter(new Qt3DRender::QParameter(QStringLiteral("reverseOffset"),
+ QVariant::fromValue(true)));
+
+ Qt3DCore::QTransform *transform = new Qt3DCore::QTransform;
+ transform->setTranslation(QVector3D(0.0f, 2.0f, -40.0f));
+ transform->setRotation(Qt3DCore::QTransform::fromAxisAndAngle(1.0f, 2.0f, 3.0f, 90.0f));
+ Qt3DRender::QGeometryRenderer *boxMesh = createCustomCube();
+ Qt3DRender::QBuffer *offsetBuffer =
+ new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, boxMesh->geometry());
+ QByteArray offsetBufferData;
+ offsetBufferData.resize(8 * 3 * sizeof(float));
+
+ float *oPtr = reinterpret_cast<float *>(offsetBufferData.data());
+ for (int i = 0; i < 8; i++) {
+ oPtr[i * 3] = float(i) / 4.0f;
+ oPtr[i * 3 + 1] = float(8 - i) / 4.0f + 2.0f;
+ oPtr[i * 3 + 2] = float((i + 4) % 8) / 4.0f;
+ }
+
+ offsetBuffer->setData(offsetBufferData);
+
+ Qt3DRender::QAttribute *customAttribute = new Qt3DRender::QAttribute();
+ customAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
+ customAttribute->setBuffer(offsetBuffer);
+ customAttribute->setDataType(Qt3DRender::QAttribute::Float);
+ customAttribute->setDataSize(3);
+ customAttribute->setByteOffset(0);
+ customAttribute->setByteStride(0);
+ customAttribute->setCount(8);
+ customAttribute->setName(QStringLiteral("vertexOffset"));
+
+ boxMesh->geometry()->addAttribute(customAttribute);
+
+ createAndAddEntity(QStringLiteral("Custom cube with on-top material"),
+ boxMesh, material, transform);
+ }
+
#ifdef VISUAL_CHECK
m_view1->setGeometry(30, 30, 400, 400);
m_view1->setRootEntity(m_sceneRoot1);
@@ -775,6 +791,47 @@ void tst_gltfPlugins::compareComponents(Qt3DCore::QComponent *c1, Qt3DCore::QCom
QVERIFY(qFuzzyCompare(v1.toFloat(), v2.toFloat()));
}
}
+ if (QString::fromLatin1(c1->metaObject()->className())
+ .endsWith(QStringLiteral("Qt3DRender::QMaterial"))) {
+ auto m1 = qobject_cast<Qt3DRender::QMaterial *>(c1);
+ auto m2 = qobject_cast<Qt3DRender::QMaterial *>(c2);
+ QVERIFY(m1);
+ QVERIFY(m2);
+ auto e1 = m1->effect();
+ auto e2 = m2->effect();
+ QVERIFY(e1);
+ QVERIFY(e2);
+ QCOMPARE(e1->objectName(), e2->objectName());
+ QCOMPARE(e1->techniques().size(), e2->techniques().size());
+
+ compareParameters(m1->parameters(), m2->parameters());
+ compareParameters(e1->parameters(), e2->parameters());
+
+ for (auto t1 : e1->techniques()) {
+ bool techMatch = false;
+ for (auto t2 : e2->techniques()) {
+ if (t1->objectName() == t2->objectName()) {
+ techMatch = true;
+ compareParameters(t1->parameters(), t2->parameters());
+ compareFilterKeys(t1->filterKeys(), t2->filterKeys());
+ compareRenderPasses(t1->renderPasses(), t2->renderPasses());
+ QCOMPARE(t1->graphicsApiFilter()->api(),
+ t2->graphicsApiFilter()->api());
+ QCOMPARE(t1->graphicsApiFilter()->profile(),
+ t2->graphicsApiFilter()->profile());
+ QCOMPARE(t1->graphicsApiFilter()->minorVersion(),
+ t2->graphicsApiFilter()->minorVersion());
+ QCOMPARE(t1->graphicsApiFilter()->majorVersion(),
+ t2->graphicsApiFilter()->majorVersion());
+ QCOMPARE(t1->graphicsApiFilter()->extensions(),
+ t2->graphicsApiFilter()->extensions());
+ QCOMPARE(t1->graphicsApiFilter()->vendor(),
+ t2->graphicsApiFilter()->vendor());
+ }
+ }
+ QVERIFY(techMatch);
+ }
+ }
}
}
}
@@ -803,6 +860,253 @@ void tst_gltfPlugins::compareAttributes(Qt3DRender::QAttribute *a1, Qt3DRender::
}
}
+void tst_gltfPlugins::compareParameters(const QVector<Qt3DRender::QParameter *> &params1,
+ const QVector<Qt3DRender::QParameter *> &params2)
+{
+ QCOMPARE(params1.size(), params2.size());
+ for (auto p1 : params1) {
+ bool pMatch = false;
+ for (auto p2 : params2) {
+ if (p1->name() == p2->name()) {
+ pMatch = true;
+ if (p1->value().type() == QVariant::Color) {
+ // Colors are imported as QVector4Ds
+ QColor color = p1->value().value<QColor>();
+ QVector4D vec = p2->value().value<QVector4D>();
+ QCOMPARE(color.redF(), vec.x());
+ QCOMPARE(color.greenF(), vec.y());
+ QCOMPARE(color.blueF(), vec.z());
+ QCOMPARE(color.alphaF(), vec.w());
+ } else if (p1->value().canConvert<Qt3DRender::QAbstractTexture *>()) {
+ QUrl u1 = getTextureUrl(p1->value().value<Qt3DRender::QAbstractTexture *>());
+ QUrl u2 = getTextureUrl(p2->value().value<Qt3DRender::QAbstractTexture *>());
+ QCOMPARE(u1.fileName(), u2.fileName());
+ } else {
+ QCOMPARE(p1->value(), p2->value());
+ }
+ }
+ }
+ QVERIFY(pMatch);
+ }
+}
+
+void tst_gltfPlugins::compareRenderPasses(const QVector<Qt3DRender::QRenderPass *> &passes1,
+ const QVector<Qt3DRender::QRenderPass *> &passes2)
+{
+ QCOMPARE(passes1.size(), passes2.size());
+ for (auto pass1 : passes1) {
+ bool passMatch = false;
+ for (auto pass2 : passes2) {
+ if (pass1->objectName() == pass2->objectName()) {
+ passMatch = true;
+ compareFilterKeys(pass1->filterKeys(), pass2->filterKeys());
+ compareParameters(pass1->parameters(), pass2->parameters());
+
+ QVector<Qt3DRender::QRenderState *> states1 = pass1->renderStates();
+ QVector<Qt3DRender::QRenderState *> states2 = pass2->renderStates();
+ QCOMPARE(states1.size(), states2.size());
+ for (auto state1 : states1) {
+ bool stateMatch = false;
+ for (auto state2 : states2) {
+ if (state1->metaObject()->className()
+ == state2->metaObject()->className()) {
+ stateMatch = true;
+ }
+ }
+ QVERIFY(stateMatch);
+ }
+
+ QCOMPARE(pass1->shaderProgram()->vertexShaderCode(),
+ pass2->shaderProgram()->vertexShaderCode());
+ QCOMPARE(pass1->shaderProgram()->fragmentShaderCode(),
+ pass2->shaderProgram()->fragmentShaderCode());
+ }
+ }
+ QVERIFY(passMatch);
+ }
+}
+
+void tst_gltfPlugins::compareFilterKeys(const QVector<Qt3DRender::QFilterKey *> &keys1,
+ const QVector<Qt3DRender::QFilterKey *> &keys2)
+{
+ QCOMPARE(keys1.size(), keys2.size());
+ for (auto k1 : keys1) {
+ bool kMatch = false;
+ for (auto k2 : keys2) {
+ if (k1->name() == k2->name()) {
+ kMatch = true;
+ QCOMPARE(k1->value(), k2->value());
+ }
+ }
+ QVERIFY(kMatch);
+ }
+}
+
+QUrl tst_gltfPlugins::getTextureUrl(Qt3DRender::QAbstractTexture *tex)
+{
+ QUrl url;
+ if (tex->textureImages().size()) {
+ Qt3DRender::QTextureImage *img =
+ qobject_cast<Qt3DRender::QTextureImage *>(
+ tex->textureImages().at(0));
+ if (img)
+ url = img->source();
+ }
+ return url;
+}
+
+Qt3DRender::QGeometryRenderer *tst_gltfPlugins::createCustomCube()
+{
+ Qt3DRender::QGeometryRenderer *boxMesh = new Qt3DRender::QGeometryRenderer;
+ Qt3DRender::QGeometry *boxGeometry = new Qt3DRender::QGeometry(boxMesh);
+ Qt3DRender::QBuffer *boxDataBuffer =
+ new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, boxGeometry);
+ Qt3DRender::QBuffer *indexDataBuffer =
+ new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, boxGeometry);
+ QByteArray vertexBufferData;
+ QByteArray indexBufferData;
+
+ vertexBufferData.resize(8 * 3 * sizeof(float));
+ indexBufferData.resize(12 * 3 * sizeof(ushort));
+
+ float dimension = 1.0f;
+
+ float *vPtr = reinterpret_cast<float *>(vertexBufferData.data());
+ vPtr[0] = -dimension; vPtr[1] = -dimension; vPtr[2] = -dimension;
+ vPtr[3] = dimension; vPtr[4] = -dimension; vPtr[5] = -dimension;
+ vPtr[6] = dimension; vPtr[7] = -dimension; vPtr[8] = dimension;
+ vPtr[9] = -dimension; vPtr[10] = -dimension; vPtr[11] = dimension;
+ vPtr[12] = -dimension; vPtr[13] = dimension; vPtr[14] = -dimension;
+ vPtr[15] = dimension; vPtr[16] = dimension; vPtr[17] = -dimension;
+ vPtr[18] = dimension; vPtr[19] = dimension; vPtr[20] = dimension;
+ vPtr[21] = -dimension; vPtr[22] = dimension; vPtr[23] = dimension;
+
+ ushort *iPtr = reinterpret_cast<ushort *>(indexBufferData.data());
+ iPtr[0] = 2; iPtr[1] = 0; iPtr[2] = 1;
+ iPtr[3] = 2; iPtr[4] = 3; iPtr[5] = 0;
+ iPtr[6] = 1; iPtr[7] = 6; iPtr[8] = 2;
+ iPtr[9] = 1; iPtr[10] = 5; iPtr[11] = 6;
+ iPtr[12] = 2; iPtr[13] = 7; iPtr[14] = 3;
+ iPtr[15] = 2; iPtr[16] = 6; iPtr[17] = 7;
+ iPtr[18] = 6; iPtr[19] = 5; iPtr[20] = 4;
+ iPtr[21] = 6; iPtr[22] = 4; iPtr[23] = 7;
+ iPtr[24] = 7; iPtr[25] = 0; iPtr[26] = 3;
+ iPtr[27] = 7; iPtr[28] = 4; iPtr[29] = 0;
+ iPtr[30] = 4; iPtr[31] = 1; iPtr[32] = 0;
+ iPtr[33] = 4; iPtr[34] = 5; iPtr[35] = 1;
+
+ boxDataBuffer->setData(vertexBufferData);
+ indexDataBuffer->setData(indexBufferData);
+
+ addPositionAttributeToGeometry(boxGeometry, boxDataBuffer, 8);
+ addIndexAttributeToGeometry(boxGeometry, indexDataBuffer, 36);
+
+ boxMesh->setInstanceCount(1);
+ boxMesh->setIndexOffset(0);
+ boxMesh->setFirstInstance(0);
+ boxMesh->setVertexCount(36);
+ boxMesh->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
+ boxMesh->setGeometry(boxGeometry);
+
+ return boxMesh;
+}
+
+Qt3DRender::QEffect *tst_gltfPlugins::createOnTopEffect()
+{
+ Qt3DRender::QEffect *effect = new Qt3DRender::QEffect;
+
+ Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique();
+ technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile);
+ technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
+ technique->graphicsApiFilter()->setMajorVersion(2);
+ technique->graphicsApiFilter()->setMinorVersion(1);
+
+ Qt3DRender::QTechnique *techniqueCore = new Qt3DRender::QTechnique();
+ techniqueCore->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
+ techniqueCore->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
+ techniqueCore->graphicsApiFilter()->setMajorVersion(3);
+ techniqueCore->graphicsApiFilter()->setMinorVersion(1);
+
+ Qt3DRender::QTechnique *techniqueES2 = new Qt3DRender::QTechnique();
+ techniqueES2->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES);
+ techniqueES2->graphicsApiFilter()->setMajorVersion(2);
+ techniqueES2->graphicsApiFilter()->setMinorVersion(0);
+ techniqueES2->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile);
+
+ Qt3DRender::QFilterKey *filterkey1 = new Qt3DRender::QFilterKey(effect);
+ Qt3DRender::QFilterKey *filterkey2 = new Qt3DRender::QFilterKey();
+ filterkey1->setName(QStringLiteral("renderingStyle"));
+ filterkey1->setValue(QStringLiteral("forward"));
+ filterkey2->setName(QStringLiteral("dummyKey"));
+ filterkey2->setValue(QStringLiteral("dummyValue"));
+
+ Qt3DRender::QParameter *parameter1 = new Qt3DRender::QParameter(QStringLiteral("handleColor"),
+ QColor(Qt::yellow));
+ Qt3DRender::QParameter *parameter2 = new Qt3DRender::QParameter(QStringLiteral("customAlpha"),
+ 1.0f);
+ Qt3DRender::QParameter *parameter3 = new Qt3DRender::QParameter(QStringLiteral("handleColor"),
+ QColor(Qt::blue));
+ Qt3DRender::QTexture2D *texture = new Qt3DRender::QTexture2D;
+ Qt3DRender::QParameter *parameter4 =
+ new Qt3DRender::QParameter(QStringLiteral("customTexture"), texture);
+ Qt3DRender::QTextureImage *ti = new Qt3DRender::QTextureImage();
+ parameter4->value().value<Qt3DRender::QAbstractTexture *>()->addTextureImage(ti);
+ ti->setSource(QUrl(QStringLiteral("qrc:/qtlogo.png")));
+
+ technique->addFilterKey(filterkey1);
+ technique->addFilterKey(filterkey2);
+ techniqueES2->addFilterKey(filterkey1);
+ techniqueES2->addFilterKey(filterkey2);
+ techniqueCore->addFilterKey(filterkey1);
+
+ technique->addParameter(parameter1);
+ technique->addParameter(parameter2);
+ technique->addParameter(parameter4);
+ techniqueES2->addParameter(parameter1);
+ techniqueES2->addParameter(parameter2);
+
+ Qt3DRender::QShaderProgram *shader = new Qt3DRender::QShaderProgram();
+ Qt3DRender::QShaderProgram *shaderES2 = new Qt3DRender::QShaderProgram();
+ shader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(
+ QUrl(QStringLiteral("qrc:/ontopmaterial.vert"))));
+ shader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(
+ QUrl(QStringLiteral("qrc:/ontopmaterial.frag"))));
+ shaderES2->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(
+ QUrl(QStringLiteral("qrc:/ontopmaterialES2.vert"))));
+ shaderES2->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(
+ QUrl(QStringLiteral("qrc:/ontopmaterialES2.frag"))));
+ shader->setObjectName(QStringLiteral("Basic shader"));
+ shaderES2->setObjectName(QStringLiteral("ES2 shader"));
+
+ Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass();
+ Qt3DRender::QRenderPass *renderPassES2 = new Qt3DRender::QRenderPass();
+ renderPass->setShaderProgram(shader);
+ renderPassES2->setShaderProgram(shaderES2);
+ renderPass->addFilterKey(filterkey2);
+ renderPass->addParameter(parameter3);
+ Qt3DRender::QColorMask *cmask = new Qt3DRender::QColorMask;
+ cmask->setRedMasked(false);
+ renderPass->addRenderState(cmask);
+ Qt3DRender::QBlendEquation *be = new Qt3DRender::QBlendEquation;
+ be->setBlendFunction(Qt3DRender::QBlendEquation::Subtract);
+ renderPass->addRenderState(be);
+ technique->addRenderPass(renderPassES2);
+ techniqueES2->addRenderPass(renderPassES2);
+ techniqueCore->addRenderPass(renderPass);
+ technique->setObjectName(QStringLiteral("Basic technique"));
+ techniqueES2->setObjectName(QStringLiteral("ES2 technique"));
+ techniqueCore->setObjectName(QStringLiteral("Core technique"));
+ renderPass->setObjectName(QStringLiteral("Basic pass"));
+ renderPassES2->setObjectName(QStringLiteral("ES2 pass"));
+
+ effect->addTechnique(technique);
+ effect->addTechnique(techniqueES2);
+ effect->addTechnique(techniqueCore);
+ effect->setObjectName(QStringLiteral("OnTopEffect"));
+
+ return effect;
+}
+
Qt3DCore::QEntity *tst_gltfPlugins::findCameraChild(Qt3DCore::QEntity *entity,
Qt3DRender::QCameraLens::ProjectionType type)
{