From 2d910d35f50280d2af2e82edf91d791525e9ada3 Mon Sep 17 00:00:00 2001
From: Inho Lee <inho.lee@qt.io>
Date: Wed, 24 Jun 2020 09:51:44 +0200
Subject: [PATCH] Quick3D: support gltf2's normal, tangent generation

This patch will fix NormalTangentMirrorTest

Task-number: QTBUG-78840
Change-Id: I4af026b47fccab8dbdb8a822f42cf0ec21709d95
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
---
 .../assetimporters/assimp/options.json        |  2 +-
 .../qssgrendercustommaterialsystem.cpp        | 37 ++++-----
 .../qssgrendercustommaterialsystem_p.h        |  7 +-
 .../rendererimpl/qssgrendererimplshaders.cpp  | 75 +++++++------------
 .../rendererimpl/qssgvertexpipelineimpl_p.h   | 70 +++++++++++++----
 5 files changed, 104 insertions(+), 87 deletions(-)

diff --git a/src/plugins/assetimporters/assimp/options.json b/src/plugins/assetimporters/assimp/options.json
index 02f24f446..75b1d5783 100644
--- a/src/plugins/assetimporters/assimp/options.json
+++ b/src/plugins/assetimporters/assimp/options.json
@@ -3,7 +3,7 @@
         "calculateTangentSpace": {
             "name": "Calculate Tangent Space",
             "description": "Calculates the tangents and bitangents for the imported meshes.",
-            "value": true,
+            "value": false,
             "type": "Boolean"
         },
         "joinIdenticalVertices": {
diff --git a/src/runtimerender/qssgrendercustommaterialsystem.cpp b/src/runtimerender/qssgrendercustommaterialsystem.cpp
index 5fbcb98ae..b0c16a148 100644
--- a/src/runtimerender/qssgrendercustommaterialsystem.cpp
+++ b/src/runtimerender/qssgrendercustommaterialsystem.cpp
@@ -315,7 +315,7 @@ void QSSGCustomMaterialVertexPipeline::assignOutput(const QByteArray &inVarName,
     vertex() << "\t" << inVarName << " = " << inVarValue << ";\n";
 }
 
-void QSSGCustomMaterialVertexPipeline::generateUVCoords(const QSSGShaderDefaultMaterialKey &inKey, quint32 inUVSet)
+void QSSGCustomMaterialVertexPipeline::generateUVCoords(const QSSGShaderDefaultMaterialKey &, quint32 inUVSet)
 {
     if (inUVSet == 0 && setCode(GenerationFlag::UVCoords))
         return;
@@ -329,15 +329,15 @@ void QSSGCustomMaterialVertexPipeline::generateUVCoords(const QSSGShaderDefaultM
     else if (inUVSet == 1)
         addInterpolationParameter("varTexCoord1", "vec3");
 
-    doGenerateUVCoords(inKey, inUVSet);
+    doGenerateUVCoords(inUVSet);
 }
 
-void QSSGCustomMaterialVertexPipeline::generateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey)
+void QSSGCustomMaterialVertexPipeline::generateWorldNormal(const QSSGShaderDefaultMaterialKey &)
 {
     if (setCode(GenerationFlag::WorldNormal))
         return;
     addInterpolationParameter("varNormal", "vec3");
-    doGenerateWorldNormal(inKey);
+    doGenerateWorldNormal();
 }
 
 void QSSGCustomMaterialVertexPipeline::generateObjectNormal()
@@ -347,7 +347,7 @@ void QSSGCustomMaterialVertexPipeline::generateObjectNormal()
     doGenerateObjectNormal();
 }
 
-void QSSGCustomMaterialVertexPipeline::generateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &inKey)
+void QSSGCustomMaterialVertexPipeline::generateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &)
 {
     if (setCode(GenerationFlag::TangentBinormal))
         return;
@@ -355,7 +355,8 @@ void QSSGCustomMaterialVertexPipeline::generateVarTangentAndBinormal(const QSSGS
     addInterpolationParameter("varBinormal", "vec3");
     addInterpolationParameter("varObjTangent", "vec3");
     addInterpolationParameter("varObjBinormal", "vec3");
-    doGenerateVarTangentAndBinormal(inKey);
+    doGenerateVarTangent();
+    doGenerateVarBinormal();
 }
 
 void QSSGCustomMaterialVertexPipeline::generateWorldPosition()
@@ -423,7 +424,7 @@ void QSSGCustomMaterialVertexPipeline::addInterpolationParameter(const QByteArra
     }
 }
 
-void QSSGCustomMaterialVertexPipeline::doGenerateUVCoords(const QSSGShaderDefaultMaterialKey &, quint32 inUVSet)
+void QSSGCustomMaterialVertexPipeline::doGenerateUVCoords(quint32 inUVSet)
 {
     Q_ASSERT(inUVSet == 0 || inUVSet == 1);
 
@@ -440,7 +441,7 @@ void QSSGCustomMaterialVertexPipeline::doGenerateUVCoords(const QSSGShaderDefaul
     }
 }
 
-void QSSGCustomMaterialVertexPipeline::doGenerateWorldNormal(const QSSGShaderDefaultMaterialKey &)
+void QSSGCustomMaterialVertexPipeline::doGenerateWorldNormal()
 {
     QSSGShaderStageGeneratorInterface &vertexGenerator(vertex());
     vertexGenerator.addIncoming("attr_norm", "vec3");
@@ -464,20 +465,20 @@ void QSSGCustomMaterialVertexPipeline::doGenerateWorldPosition()
     assignOutput("varWorldPos", "worldPos.xyz");
 }
 
-void QSSGCustomMaterialVertexPipeline::doGenerateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &)
+void QSSGCustomMaterialVertexPipeline::doGenerateVarTangent()
 {
     vertex().addIncoming("attr_textan", "vec3");
-    vertex().addIncoming("attr_binormal", "vec3");
 
-    vertex() << "\tvarTangent = normalMatrix * attr_textan;"
-             << "\n"
-             << "\tvarBinormal = normalMatrix * attr_binormal;"
-             << "\n";
+    vertex() << "\tvarTangent = normalMatrix * attr_textan;\n";
+    vertex() << "\tvarObjTangent = attr_textan;\n";
+}
+
+void QSSGCustomMaterialVertexPipeline::doGenerateVarBinormal()
+{
+    vertex().addIncoming("attr_binormal", "vec3");
 
-    vertex() << "\tvarObjTangent = attr_textan;"
-             << "\n"
-             << "\tvarObjBinormal = attr_binormal;"
-             << "\n";
+    vertex() << "\tvarBinormal = normalMatrix * attr_binormal;\n";
+    vertex() << "\tvarObjBinormal = attr_binormal;\n";
 }
 
 void QSSGCustomMaterialVertexPipeline::doGenerateVertexColor(const QSSGShaderDefaultMaterialKey &)
diff --git a/src/runtimerender/qssgrendercustommaterialsystem_p.h b/src/runtimerender/qssgrendercustommaterialsystem_p.h
index 07544c38b..466ab7276 100644
--- a/src/runtimerender/qssgrendercustommaterialsystem_p.h
+++ b/src/runtimerender/qssgrendercustommaterialsystem_p.h
@@ -233,11 +233,12 @@ struct Q_QUICK3DRUNTIMERENDER_EXPORT QSSGCustomMaterialVertexPipeline : public Q
     virtual void endFragmentGeneration(bool customShader) override;
     virtual QSSGShaderStageGeneratorInterface &activeStage() override;
     virtual void addInterpolationParameter(const QByteArray &inName, const QByteArray &inType) override;
-    virtual void doGenerateUVCoords(const QSSGShaderDefaultMaterialKey &inKey, quint32 inUVSet) override;
-    virtual void doGenerateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey) override;
+    virtual void doGenerateUVCoords(quint32 inUVSet) override;
+    virtual void doGenerateWorldNormal() override;
     virtual void doGenerateObjectNormal() override;
     virtual void doGenerateWorldPosition() override;
-    virtual void doGenerateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &inKey) override;
+    virtual void doGenerateVarTangent() override;
+    virtual void doGenerateVarBinormal() override;
     virtual void doGenerateVertexColor(const QSSGShaderDefaultMaterialKey &inKey) override;
 };
 QT_END_NAMESPACE
diff --git a/src/runtimerender/rendererimpl/qssgrendererimplshaders.cpp b/src/runtimerender/rendererimpl/qssgrendererimplshaders.cpp
index 28cd6f991..4b81f18ba 100644
--- a/src/runtimerender/rendererimpl/qssgrendererimplshaders.cpp
+++ b/src/runtimerender/rendererimpl/qssgrendererimplshaders.cpp
@@ -308,43 +308,25 @@ struct QSSGSubsetMaterialVertexPipeline : public QSSGVertexPipelineImpl
     {
         vertex() << "    " << inVarName << " = " << inVarValue << ";\n";
     }
-    void doGenerateUVCoords(const QSSGShaderDefaultMaterialKey &inKey, quint32 inUVSet = 0) override
+    void doGenerateUVCoords(quint32 inUVSet = 0) override
     {
         Q_ASSERT(inUVSet == 0 || inUVSet == 1);
 
         if (inUVSet == 0) {
-            const bool meshHasTexCoord0 = renderer.defaultMaterialShaderKeyProperties().m_vertexAttributes.getBitValue(
-                        QSSGShaderKeyVertexAttribute::TexCoord0, inKey);
-            if (meshHasTexCoord0)
-                vertex().addIncoming("attr_uv0", "vec2");
-            else
-                vertex().append("    vec2 attr_uv0 = vec2(0.0);");
-            vertex() << "    varTexCoord0 = attr_uv0;"
-                     << "\n";
+            vertex().addIncoming("attr_uv0", "vec2");
+            vertex() << "    varTexCoord0 = attr_uv0;\n";
         } else if (inUVSet == 1) {
-            const bool meshHasTexCoord1 = renderer.defaultMaterialShaderKeyProperties().m_vertexAttributes.getBitValue(
-                        QSSGShaderKeyVertexAttribute::TexCoord1, inKey);
-            if (meshHasTexCoord1)
-                vertex().addIncoming("attr_uv1", "vec2");
-            else
-                vertex().append("    vec2 attr_uv1 = vec2(0.0);");
-            vertex() << "    varTexCoord1 = attr_uv1;"
-                     << "\n";
+            vertex().addIncoming("attr_uv1", "vec2");
+            vertex() << "    varTexCoord1 = attr_uv1;\n";
         }
     }
 
     // fragment shader expects varying vertex normal
     // lighting in vertex pipeline expects world_normal
-    void doGenerateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey) override
+    void doGenerateWorldNormal() override
     {
-        const bool meshHasNormals = renderer.defaultMaterialShaderKeyProperties().m_vertexAttributes.getBitValue(
-                QSSGShaderKeyVertexAttribute::Normal, inKey);
-
         QSSGShaderStageGeneratorInterface &vertexGenerator(vertex());
-        if (meshHasNormals)
-            vertexGenerator.addIncoming("attr_norm", "vec3");
-        else
-            vertexGenerator.append("    vec3 attr_norm = vec3(0.0);");
+        vertexGenerator.addIncoming("attr_norm", "vec3");
         vertexGenerator.addUniform("normalMatrix", "mat3");
         if (hasTessellation() == false) {
             vertexGenerator.append("    vec3 world_normal = normalize(normalMatrix * attr_norm).xyz;");
@@ -362,36 +344,26 @@ struct QSSGSubsetMaterialVertexPipeline : public QSSGVertexPipelineImpl
         assignOutput("varWorldPos", "local_model_world_position");
     }
 
-    void doGenerateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &inKey) override
+    void doGenerateVarTangent() override
     {
-        const bool meshHasTangents = renderer.defaultMaterialShaderKeyProperties().m_vertexAttributes.getBitValue(
-                    QSSGShaderKeyVertexAttribute::Tangent, inKey);
-        const bool meshHasBinormals = renderer.defaultMaterialShaderKeyProperties().m_vertexAttributes.getBitValue(
-                    QSSGShaderKeyVertexAttribute::Binormal, inKey);
+        vertex().addIncoming("attr_textan", "vec3");
 
-        if (meshHasTangents)
-            vertex().addIncoming("attr_textan", "vec3");
+        bool hasNPatchTessellation = tessMode == TessellationModeValues::NPatch;
+        if (!hasNPatchTessellation)
+            vertex() << "    varTangent = normalMatrix * attr_textan;\n";
         else
-            vertex() << "    vec3 attr_textan = vec3(0.0);\n";
+            vertex() << "    varTangent = attr_textan;\n";
+    }
 
-        if (meshHasBinormals)
-            vertex().addIncoming("attr_binormal", "vec3");
-        else
-            vertex() << "    vec3 attr_binormal = vec3(0.0);\n";
+    void doGenerateVarBinormal() override
+    {
+        vertex().addIncoming("attr_binormal", "vec3");
 
         bool hasNPatchTessellation = tessMode == TessellationModeValues::NPatch;
-
-        if (!hasNPatchTessellation) {
-            vertex() << "    varTangent = normalMatrix * attr_textan;"
-                     << "\n"
-                     << "    varBinormal = normalMatrix * attr_binormal;"
-                     << "\n";
-        } else {
-            vertex() << "    varTangent = attr_textan;"
-                     << "\n"
-                     << "    varBinormal = attr_binormal;"
-                     << "\n";
-        }
+        if (!hasNPatchTessellation)
+            vertex() << "    varBinormal = normalMatrix * attr_binormal;\n";
+        else
+            vertex() << "    varBinormal = attr_binormal;\n";
     }
 
     void doGenerateVertexColor(const QSSGShaderDefaultMaterialKey &inKey) override
@@ -405,6 +377,11 @@ struct QSSGSubsetMaterialVertexPipeline : public QSSGVertexPipelineImpl
         vertex().append("    varColor = attr_color;");
     }
 
+    bool hasAttributeInKey(QSSGShaderKeyVertexAttribute::VertexAttributeBits inAttr, const QSSGShaderDefaultMaterialKey &inKey) override
+    {
+        return renderer.defaultMaterialShaderKeyProperties().m_vertexAttributes.getBitValue(inAttr, inKey);
+    }
+
     void endVertexGeneration(bool customShader) override
     {
 
diff --git a/src/runtimerender/rendererimpl/qssgvertexpipelineimpl_p.h b/src/runtimerender/rendererimpl/qssgvertexpipelineimpl_p.h
index 9852bc1b4..db3c76507 100644
--- a/src/runtimerender/rendererimpl/qssgvertexpipelineimpl_p.h
+++ b/src/runtimerender/rendererimpl/qssgvertexpipelineimpl_p.h
@@ -44,6 +44,7 @@
 
 #include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
 #include <QtQuick3DRuntimeRender/private/qssgrendertessmodevalues_p.h>
+#include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h>
 
 #include <QtCore/QSharedPointer>
 
@@ -236,12 +237,21 @@ struct QSSGVertexPipelineImpl : public QSSGDefaultMaterialVertexPipelineInterfac
 
         Q_ASSERT(inUVSet == 0 || inUVSet == 1);
 
-        if (inUVSet == 0)
-            addInterpolationParameter("varTexCoord0", "vec2");
-        else if (inUVSet == 1)
-            addInterpolationParameter("varTexCoord1", "vec2");
-
-        doGenerateUVCoords(inKey, inUVSet);
+        if (inUVSet == 0) {
+            if (hasAttributeInKey(QSSGShaderKeyVertexAttribute::TexCoord0, inKey)) {
+                addInterpolationParameter("varTexCoord0", "vec2");
+                doGenerateUVCoords(inUVSet);
+            } else {
+                fragment() << "    vec2 varTexCoord0 = vec2(0.0);\n";
+            }
+        } else if (inUVSet == 1) {
+            if (hasAttributeInKey(QSSGShaderKeyVertexAttribute::TexCoord1, inKey)) {
+                addInterpolationParameter("varTexCoord1", "vec2");
+                doGenerateUVCoords(inUVSet);
+            } else {
+                fragment() << "    vec2 varTexCoord1 = vec2(0.0);\n";
+            }
+        }
     }
     void generateEnvMapReflection(const QSSGShaderDefaultMaterialKey &inKey) override
     {
@@ -284,8 +294,14 @@ struct QSSGVertexPipelineImpl : public QSSGDefaultMaterialVertexPipelineInterfac
     {
         if (setCode(GenerationFlag::WorldNormal))
             return;
-        addInterpolationParameter("varNormal", "vec3");
-        doGenerateWorldNormal(inKey);
+
+        if (hasAttributeInKey(QSSGShaderKeyVertexAttribute::Normal, inKey)) {
+            addInterpolationParameter("varNormal", "vec3");
+            doGenerateWorldNormal();
+        } else {
+            generateWorldPosition();
+            fragment().append("    vec3 varNormal = cross(dFdx(varWorldPos), dFdy(varWorldPos));");
+        }
         fragment().append("    vec3 world_normal = normalize( varNormal );");
     }
     void generateObjectNormal() override
@@ -310,11 +326,26 @@ struct QSSGVertexPipelineImpl : public QSSGDefaultMaterialVertexPipelineInterfac
     {
         if (setCode(GenerationFlag::TangentBinormal))
             return;
-        addInterpolationParameter("varTangent", "vec3");
-        addInterpolationParameter("varBinormal", "vec3");
-        doGenerateVarTangentAndBinormal(inKey);
-        fragment() << "    vec3 tangent = normalize(varTangent);\n"
-                   << "    vec3 binormal = normalize(varBinormal);\n";
+
+        // I assume that there is no mesh having only binormal without tangent
+        // since it is an abnormal case
+        if (hasAttributeInKey(QSSGShaderKeyVertexAttribute::Tangent, inKey)) {
+            const bool hasBinormal = hasAttributeInKey(QSSGShaderKeyVertexAttribute::Binormal, inKey);
+            addInterpolationParameter("varTangent", "vec3");
+            doGenerateVarTangent();
+            fragment() << "    vec3 tangent = normalize(varTangent);\n";
+
+            if (hasBinormal) {
+                addInterpolationParameter("varBinormal", "vec3");
+                doGenerateVarBinormal();
+                fragment() << "    vec3 binormal = normalize(varBinormal);\n";
+            } else {
+                fragment() << "    vec3 binormal = vec3(0.0);\n";
+            }
+        } else {
+            fragment() << "    vec3 tangent = vec3(0.0);\n"
+                       << "    vec3 binormal = vec3(0.0);\n";
+        }
     }
     void generateVertexColor(const QSSGShaderDefaultMaterialKey &inKey) override
     {
@@ -376,12 +407,19 @@ struct QSSGVertexPipelineImpl : public QSSGDefaultMaterialVertexPipelineInterfac
     virtual QSSGShaderStageGeneratorInterface &activeStage() = 0;
     virtual void addInterpolationParameter(const QByteArray &inParamName, const QByteArray &inParamType) = 0;
 
-    virtual void doGenerateUVCoords(const QSSGShaderDefaultMaterialKey &inKey, quint32 inUVSet) = 0;
-    virtual void doGenerateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey) = 0;
+    virtual void doGenerateUVCoords(quint32 inUVSet) = 0;
+    virtual void doGenerateWorldNormal() = 0;
     virtual void doGenerateObjectNormal() = 0;
     virtual void doGenerateWorldPosition() = 0;
-    virtual void doGenerateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &inKey) = 0;
+    virtual void doGenerateVarTangent() = 0;
+    virtual void doGenerateVarBinormal() = 0;
     virtual void doGenerateVertexColor(const QSSGShaderDefaultMaterialKey &inKey) = 0;
+    virtual bool hasAttributeInKey(QSSGShaderKeyVertexAttribute::VertexAttributeBits inAttr, const QSSGShaderDefaultMaterialKey &inKey) {
+        // it returns true by default
+        Q_UNUSED(inAttr)
+        Q_UNUSED(inKey)
+        return true;
+    }
 };
 QT_END_NAMESPACE
 
-- 
GitLab