| /**************************************************************************** |
| ** |
| ** Copyright (C) 2008-2012 NVIDIA Corporation. |
| ** Copyright (C) 2019 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of Qt Quick 3D. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #ifndef QSSG_VERTEX_PIPELINE_IMPL_H |
| #define QSSG_VERTEX_PIPELINE_IMPL_H |
| |
| // |
| // W A R N I N G |
| // ------------- |
| // |
| // This file is not part of the Qt API. It exists purely as an |
| // implementation detail. This header file may change from version to |
| // version without notice, or even be removed. |
| // |
| // We mean it. |
| // |
| |
| #include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendertessmodevalues_p.h> |
| |
| #include <QtCore/QSharedPointer> |
| |
| QT_BEGIN_NAMESPACE |
| // Baseclass for the vertex pipelines to be sure we have consistent implementations. |
| struct QSSGVertexPipelineImpl : public QSSGDefaultMaterialVertexPipelineInterface |
| { |
| enum class GenerationFlag |
| { |
| UVCoords = 1, |
| EnvMapReflection = 1 << 1, |
| ViewVector = 1 << 2, |
| WorldNormal = 1 << 3, |
| ObjectNormal = 1 << 4, |
| WorldPosition = 1 << 5, |
| TangentBinormal = 1 << 6, |
| UVCoords1 = 1 << 7, |
| VertexColor = 1 << 8, |
| }; |
| |
| typedef TStrTableStrMap::const_iterator TParamIter; |
| typedef QFlags<GenerationFlag> GenerationFlags; |
| |
| QSSGRef<QSSGMaterialShaderGeneratorInterface> m_materialGenerator; |
| QSSGRef<QSSGShaderProgramGeneratorInterface> m_programGenerator; |
| QString m_tempString; |
| |
| GenerationFlags m_generationFlags; |
| bool m_wireframe; |
| TStrTableStrMap m_interpolationParameters; |
| quint32 m_displacementIdx; |
| QSSGRenderableImage *m_displacementImage; |
| QList<QByteArray> m_addedFunctions; |
| |
| QSSGVertexPipelineImpl(QSSGRef<QSSGMaterialShaderGeneratorInterface> inMaterial, |
| QSSGRef<QSSGShaderProgramGeneratorInterface> inProgram, |
| bool inWireframe /* only works if tessellation is true */) |
| |
| : m_materialGenerator(inMaterial) |
| , m_programGenerator(inProgram) |
| , m_wireframe(inWireframe) |
| , m_displacementIdx(0) |
| , m_displacementImage(nullptr) |
| { |
| } |
| |
| // Trues true if the code was *not* set. |
| bool setCode(GenerationFlag inCode) |
| { |
| if (m_generationFlags & inCode) |
| return true; |
| m_generationFlags |= inCode; |
| return false; |
| } |
| bool hasCode(GenerationFlag inCode) { return (m_generationFlags & inCode); } |
| QSSGRef<QSSGShaderProgramGeneratorInterface> programGenerator() { return m_programGenerator; } |
| |
| QSSGShaderStageGeneratorInterface &vertex() |
| { |
| return *programGenerator()->getStage(QSSGShaderGeneratorStage::Vertex); |
| } |
| QSSGShaderStageGeneratorInterface &tessControl() |
| { |
| return *programGenerator()->getStage(QSSGShaderGeneratorStage::TessControl); |
| } |
| QSSGShaderStageGeneratorInterface &tessEval() |
| { |
| return *programGenerator()->getStage(QSSGShaderGeneratorStage::TessEval); |
| } |
| QSSGShaderStageGeneratorInterface &geometry() |
| { |
| return *programGenerator()->getStage(QSSGShaderGeneratorStage::Geometry); |
| } |
| QSSGShaderStageGeneratorInterface &fragment() |
| { |
| return *programGenerator()->getStage(QSSGShaderGeneratorStage::Fragment); |
| } |
| QSSGRef<QSSGMaterialShaderGeneratorInterface> materialGenerator() { return m_materialGenerator; } |
| |
| void setupDisplacement(quint32 displacementImageIdx, QSSGRenderableImage *displacementImage) |
| { |
| m_displacementIdx = displacementImageIdx; |
| m_displacementImage = displacementImage; |
| } |
| |
| bool hasTessellation() const { return m_programGenerator->getEnabledStages() & QSSGShaderGeneratorStage::TessEval; } |
| bool hasGeometryStage() const { return m_programGenerator->getEnabledStages() & QSSGShaderGeneratorStage::Geometry; } |
| bool hasDisplacment() const { return m_displacementImage != nullptr; } |
| |
| void initializeWireframeGeometryShader() |
| { |
| if (m_wireframe && programGenerator()->getStage(QSSGShaderGeneratorStage::Geometry) |
| && programGenerator()->getStage(QSSGShaderGeneratorStage::TessEval)) { |
| QSSGShaderStageGeneratorInterface &geometryShader(*programGenerator()->getStage(QSSGShaderGeneratorStage::Geometry)); |
| // currently geometry shader is only used for drawing wireframe |
| if (m_wireframe) { |
| geometryShader.addUniform("viewportMatrix", "mat4"); |
| geometryShader.addOutgoing("varEdgeDistance", "vec3"); |
| geometryShader.append("layout (triangles) in;"); |
| geometryShader.append("layout (triangle_strip, max_vertices = 3) out;"); |
| geometryShader.append("void main() {"); |
| |
| // how this all work see |
| // http://developer.download.nvidia.com/SDK/10.5/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf |
| |
| geometryShader.append("// project points to screen space\n" |
| " vec3 p0 = vec3(viewportMatrix * (gl_in[0].gl_Position / " |
| "gl_in[0].gl_Position.w));\n" |
| " vec3 p1 = vec3(viewportMatrix * (gl_in[1].gl_Position / " |
| "gl_in[1].gl_Position.w));\n" |
| " vec3 p2 = vec3(viewportMatrix * (gl_in[2].gl_Position / " |
| "gl_in[2].gl_Position.w));\n" |
| "// compute triangle heights\n" |
| " float e1 = length(p1 - p2);\n" |
| " float e2 = length(p2 - p0);\n" |
| " float e3 = length(p1 - p0);\n" |
| " float alpha = acos( (e2*e2 + e3*e3 - e1*e1) / (2.0*e2*e3) );\n" |
| " float beta = acos( (e1*e1 + e3*e3 - e2*e2) / (2.0*e1*e3) );\n" |
| " float ha = abs( e3 * sin( beta ) );\n" |
| " float hb = abs( e3 * sin( alpha ) );\n" |
| " float hc = abs( e2 * sin( alpha ) );\n"); |
| } |
| } |
| } |
| |
| void finalizeWireframeGeometryShader() |
| { |
| QSSGShaderStageGeneratorInterface &geometryShader(*programGenerator()->getStage(QSSGShaderGeneratorStage::Geometry)); |
| |
| if (m_wireframe == true && programGenerator()->getStage(QSSGShaderGeneratorStage::Geometry) |
| && programGenerator()->getStage(QSSGShaderGeneratorStage::TessEval)) { |
| const char *theExtension("TE["); |
| // we always assume triangles |
| for (int i = 0; i < 3; i++) { |
| char buf[10]; |
| sprintf(buf, "%d", i); |
| for (TStrTableStrMap::iterator iter = m_interpolationParameters.begin(), end = m_interpolationParameters.end(); |
| iter != end; |
| ++iter) { |
| geometryShader << " " << iter.key() << " = " << iter.key() << theExtension << buf << "];\n"; |
| } |
| |
| geometryShader << " gl_Position = gl_in[" << buf << "].gl_Position;\n"; |
| // the triangle distance is interpolated through the shader stage |
| if (i == 0) { |
| geometryShader << "\n varEdgeDistance = vec3(ha*" |
| << "gl_in[" << buf << "].gl_Position.w, 0.0, 0.0);\n"; |
| } else if (i == 1) { |
| geometryShader << "\n varEdgeDistance = vec3(0.0, hb*" |
| << "gl_in[" << buf << "].gl_Position.w, 0.0);\n"; |
| } else if (i == 2) { |
| geometryShader << "\n varEdgeDistance = vec3(0.0, 0.0, hc*" |
| << "gl_in[" << buf << "].gl_Position.w);\n"; |
| } |
| |
| // submit vertex |
| geometryShader << " EmitVertex();\n"; |
| } |
| // end primitive |
| geometryShader << " EndPrimitive();\n"; |
| } |
| } |
| |
| virtual void setupTessIncludes(QSSGShaderGeneratorStage inStage, TessellationModeValues inTessMode) |
| { |
| QSSGShaderStageGeneratorInterface &tessShader(*programGenerator()->getStage(inStage)); |
| |
| // depending on the selected tessellation mode chose program |
| switch (inTessMode) { |
| case TessellationModeValues::Phong: |
| tessShader.addInclude("tessellationPhong.glsllib"); |
| break; |
| case TessellationModeValues::NPatch: |
| tessShader.addInclude("tessellationNPatch.glsllib"); |
| break; |
| default: |
| Q_ASSERT(false); // fallthrough intentional |
| case TessellationModeValues::Linear: |
| tessShader.addInclude("tessellationLinear.glsllib"); |
| break; |
| } |
| } |
| |
| void generateUVCoords(quint32 inUVSet = 0) override |
| { |
| if (inUVSet == 0 && setCode(GenerationFlag::UVCoords)) |
| return; |
| if (inUVSet == 1 && setCode(GenerationFlag::UVCoords1)) |
| return; |
| |
| Q_ASSERT(inUVSet == 0 || inUVSet == 1); |
| |
| if (inUVSet == 0) |
| addInterpolationParameter("varTexCoord0", "vec2"); |
| else if (inUVSet == 1) |
| addInterpolationParameter("varTexCoord1", "vec2"); |
| |
| doGenerateUVCoords(inUVSet); |
| } |
| void generateEnvMapReflection() override |
| { |
| if (setCode(GenerationFlag::EnvMapReflection)) |
| return; |
| |
| generateWorldPosition(); |
| generateWorldNormal(); |
| QSSGShaderStageGeneratorInterface &activeGenerator(activeStage()); |
| activeGenerator.addInclude("viewProperties.glsllib"); |
| addInterpolationParameter("var_object_to_camera", "vec3"); |
| |
| activeGenerator.append(" var_object_to_camera = normalize( local_model_world_position " |
| "- cameraPosition );"); |
| |
| // World normal cannot be relied upon in the vertex shader because of bump maps. |
| fragment().append(" vec3 environment_map_reflection = reflect( " |
| "normalize(var_object_to_camera), world_normal.xyz );"); |
| fragment().append(" environment_map_reflection *= vec3( 0.5, 0.5, 0 );"); |
| fragment().append(" environment_map_reflection += vec3( 0.5, 0.5, 1.0 );"); |
| } |
| void generateViewVector() override |
| { |
| if (setCode(GenerationFlag::ViewVector)) |
| return; |
| generateWorldPosition(); |
| QSSGShaderStageGeneratorInterface &activeGenerator(activeStage()); |
| activeGenerator.addInclude("viewProperties.glsllib"); |
| addInterpolationParameter("varViewVector", "vec3"); |
| |
| activeGenerator.append(" vec3 local_view_vector = normalize(cameraPosition - " |
| "local_model_world_position);"); |
| assignOutput("varViewVector", "local_view_vector"); |
| fragment() << " vec3 view_vector = normalize(varViewVector);\n"; |
| } |
| |
| // fragment shader expects varying vertex normal |
| // lighting in vertex pipeline expects world_normal |
| void generateWorldNormal() override |
| { |
| if (setCode(GenerationFlag::WorldNormal)) |
| return; |
| addInterpolationParameter("varNormal", "vec3"); |
| doGenerateWorldNormal(); |
| fragment().append(" vec3 world_normal = normalize( varNormal );"); |
| } |
| void generateObjectNormal() override |
| { |
| if (setCode(GenerationFlag::ObjectNormal)) |
| return; |
| doGenerateObjectNormal(); |
| fragment().append(" vec3 object_normal = normalize(varObjectNormal);"); |
| } |
| void generateWorldPosition() override |
| { |
| if (setCode(GenerationFlag::WorldPosition)) |
| return; |
| |
| activeStage().addUniform("modelMatrix", "mat4"); |
| addInterpolationParameter("varWorldPos", "vec3"); |
| doGenerateWorldPosition(); |
| |
| assignOutput("varWorldPos", "local_model_world_position"); |
| } |
| void generateVarTangentAndBinormal() override |
| { |
| if (setCode(GenerationFlag::TangentBinormal)) |
| return; |
| addInterpolationParameter("varTangent", "vec3"); |
| addInterpolationParameter("varBinormal", "vec3"); |
| doGenerateVarTangentAndBinormal(); |
| fragment() << " vec3 tangent = normalize(varTangent);\n" |
| << " vec3 binormal = normalize(varBinormal);\n"; |
| } |
| void generateVertexColor() override |
| { |
| if (setCode(GenerationFlag::VertexColor)) |
| return; |
| addInterpolationParameter("varColor", "vec3"); |
| doGenerateVertexColor(); |
| fragment().append(" vec3 vertColor = varColor;"); |
| } |
| |
| bool hasActiveWireframe() override { return m_wireframe; } |
| |
| void addIncoming(const QByteArray &name, const QByteArray &type) override { activeStage().addIncoming(name, type); } |
| |
| void addOutgoing(const QByteArray &name, const QByteArray &type) override { addInterpolationParameter(name, type); } |
| |
| void addUniform(const QByteArray &name, const QByteArray &type) override { activeStage().addUniform(name, type); } |
| |
| void addInclude(const QByteArray &name) override { activeStage().addInclude(name); } |
| |
| void addFunction(const QByteArray &functionName) override |
| { |
| if (!m_addedFunctions.contains(functionName)) { |
| m_addedFunctions.push_back(functionName); |
| QByteArray includeName = "func" + functionName + ".glsllib"; |
| addInclude(includeName); |
| } |
| } |
| |
| void addConstantBuffer(const QByteArray &name, const QByteArray &layout) override |
| { |
| activeStage().addConstantBuffer(name, layout); |
| } |
| void addConstantBufferParam(const QByteArray &cbName, const QByteArray ¶mName, const QByteArray &type) override |
| { |
| activeStage().addConstantBufferParam(cbName, paramName, type); |
| } |
| |
| QSSGShaderStageGeneratorInterface &operator<<(const QByteArray &data) override |
| { |
| activeStage() << data; |
| return *this; |
| } |
| |
| void append(const QByteArray &data) override { activeStage().append(data); } |
| |
| QSSGShaderGeneratorStage stage() const override |
| { |
| return const_cast<QSSGVertexPipelineImpl *>(this)->activeStage().stage(); |
| } |
| |
| void beginVertexGeneration(quint32 displacementImageIdx, QSSGRenderableImage *displacementImage) override = 0; |
| void assignOutput(const QByteArray &inVarName, const QByteArray &inVarValueExpr) override = 0; |
| void endVertexGeneration(bool customShader) override = 0; |
| |
| void beginFragmentGeneration() override = 0; |
| void endFragmentGeneration(bool customShader) override = 0; |
| |
| virtual QSSGShaderStageGeneratorInterface &activeStage() = 0; |
| virtual void addInterpolationParameter(const QByteArray &inParamName, const QByteArray &inParamType) = 0; |
| |
| virtual void doGenerateUVCoords(quint32 inUVSet) = 0; |
| virtual void doGenerateWorldNormal() = 0; |
| virtual void doGenerateObjectNormal() = 0; |
| virtual void doGenerateWorldPosition() = 0; |
| virtual void doGenerateVarTangentAndBinormal() = 0; |
| virtual void doGenerateVertexColor() = 0; |
| }; |
| QT_END_NAMESPACE |
| |
| #endif |