| 1 | // Copyright (C) 2008-2012 NVIDIA Corporation. |
| 2 | // Copyright (C) 2019 The Qt Company Ltd. |
| 3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
| 4 | |
| 5 | #include "qssgvertexpipelineimpl_p.h" |
| 6 | |
| 7 | #include <QtCore/qregularexpression.h> |
| 8 | |
| 9 | #include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h> |
| 10 | #include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h> |
| 11 | #include "../qssgrendercontextcore.h" |
| 12 | #include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h> |
| 13 | #include <QtQuick3DRuntimeRender/private/qssgrendershaderlibrarymanager_p.h> |
| 14 | #include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h> |
| 15 | #include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h> |
| 16 | #include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h> |
| 17 | |
| 18 | QT_BEGIN_NAMESPACE |
| 19 | |
| 20 | QSSGMaterialVertexPipeline::QSSGMaterialVertexPipeline(QSSGProgramGenerator &programGen, |
| 21 | const QSSGShaderDefaultMaterialKeyProperties &materialProperties, |
| 22 | QSSGShaderMaterialAdapter *materialAdapter) |
| 23 | : m_programGenerator(&programGen) |
| 24 | , defaultMaterialShaderKeyProperties(materialProperties) |
| 25 | , materialAdapter(materialAdapter) |
| 26 | , hasCustomShadedMain(false) |
| 27 | , skipCustomFragmentSnippet(false) |
| 28 | { |
| 29 | } |
| 30 | |
| 31 | static inline void insertProcessorArgsFragmentMain(QByteArray &snippet, const char *argKey, const char* (*argListFunc)(), |
| 32 | QSSGShaderMaterialAdapter *materialAdapter = nullptr, bool isSharedInout = false) |
| 33 | { |
| 34 | const int argKeyLen = int(strlen(s: argKey)); |
| 35 | const int argKeyPos = snippet.indexOf(bv: argKey); |
| 36 | if (argKeyPos >= 0) { |
| 37 | QByteArray flexArgs; |
| 38 | if (materialAdapter) { |
| 39 | if (materialAdapter->isClearcoatEnabled()) { |
| 40 | flexArgs += QByteArrayLiteral(", inout float CLEARCOAT_AMOUNT, inout float CLEARCOAT_FRESNEL_POWER, inout float CLEARCOAT_ROUGHNESS, inout vec3 CLEARCOAT_NORMAL" ); |
| 41 | if (materialAdapter->isClearcoatFresnelScaleBiasEnabled()) |
| 42 | flexArgs += QByteArrayLiteral(", inout float CLEARCOAT_FRESNEL_SCALE, inout float CLEARCOAT_FRESNEL_BIAS" ); |
| 43 | } |
| 44 | if (materialAdapter->isFresnelScaleBiasEnabled()) |
| 45 | flexArgs += QByteArrayLiteral(", inout float FRESNEL_SCALE, inout float FRESNEL_BIAS" ); |
| 46 | if (materialAdapter->isTransmissionEnabled()) |
| 47 | flexArgs += QByteArrayLiteral(", inout float TRANSMISSION_FACTOR, inout float THICKNESS_FACTOR, inout vec3 ATTENUATION_COLOR, inout float ATTENUATION_DISTANCE" ); |
| 48 | if (materialAdapter->usesSharedVariables()) { |
| 49 | const char *inoutString = isSharedInout ? ", inout " : ", in " ; |
| 50 | flexArgs += inoutString + QByteArrayLiteral("QT_SHARED_VARS SHARED" ); |
| 51 | } |
| 52 | } |
| 53 | snippet = snippet.left(n: argKeyPos) + argListFunc() + flexArgs + snippet.mid(index: argKeyPos + argKeyLen); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | static inline void insertProcessorArgs(QByteArray &snippet, const char *argKey, const char* (*argListFunc)(), |
| 58 | QSSGShaderMaterialAdapter *materialAdapter = nullptr, bool isSharedInout = false) |
| 59 | { |
| 60 | const int argKeyLen = int(strlen(s: argKey)); |
| 61 | const int argKeyPos = snippet.indexOf(bv: argKey); |
| 62 | if (argKeyPos >= 0) { |
| 63 | QByteArray flexArgs; |
| 64 | if (materialAdapter && materialAdapter->usesSharedVariables()) { |
| 65 | const char *inoutString = isSharedInout ? ", inout " : ", in " ; |
| 66 | flexArgs += inoutString + QByteArrayLiteral("QT_SHARED_VARS SHARED" ); |
| 67 | } |
| 68 | snippet = snippet.left(n: argKeyPos) + argListFunc() + flexArgs + snippet.mid(index: argKeyPos + argKeyLen); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | static inline void insertDirectionalLightProcessorArgs(QByteArray &snippet, QSSGShaderMaterialAdapter *materialAdapter) |
| 73 | { |
| 74 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_DIRECTIONAL_LIGHT%*/" , argListFunc: QSSGMaterialShaderGenerator::directionalLightProcessorArgumentList, materialAdapter, isSharedInout: true); |
| 75 | } |
| 76 | |
| 77 | static inline void insertPointLightProcessorArgs(QByteArray &snippet, QSSGShaderMaterialAdapter *materialAdapter) |
| 78 | { |
| 79 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_POINT_LIGHT%*/" , argListFunc: QSSGMaterialShaderGenerator::pointLightProcessorArgumentList, materialAdapter, isSharedInout: true); |
| 80 | } |
| 81 | |
| 82 | static inline void insertSpotLightProcessorArgs(QByteArray &snippet, QSSGShaderMaterialAdapter *materialAdapter) |
| 83 | { |
| 84 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_SPOT_LIGHT%*/" , argListFunc: QSSGMaterialShaderGenerator::spotLightProcessorArgumentList, materialAdapter, isSharedInout: true); |
| 85 | } |
| 86 | |
| 87 | static inline void insertAmbientLightProcessorArgs(QByteArray &snippet, QSSGShaderMaterialAdapter *materialAdapter) |
| 88 | { |
| 89 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_AMBIENT_LIGHT%*/" , argListFunc: QSSGMaterialShaderGenerator::ambientLightProcessorArgumentList, materialAdapter, isSharedInout: true); |
| 90 | } |
| 91 | |
| 92 | static inline void insertIblProbeProcessorArgs(QByteArray &snippet, QSSGShaderMaterialAdapter *materialAdapter) |
| 93 | { |
| 94 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_IBL_PROBE%*/" , argListFunc: QSSGMaterialShaderGenerator::iblProbeProcessorArgumentList, materialAdapter, isSharedInout: true); |
| 95 | } |
| 96 | |
| 97 | static inline void insertSpecularLightProcessorArgs(QByteArray &snippet, QSSGShaderMaterialAdapter *materialAdapter) |
| 98 | { |
| 99 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_SPECULAR_LIGHT%*/" , argListFunc: QSSGMaterialShaderGenerator::specularLightProcessorArgumentList, materialAdapter, isSharedInout: true); |
| 100 | } |
| 101 | |
| 102 | static inline void insertFragmentMainArgs(QByteArray &snippet, QSSGShaderMaterialAdapter *materialAdapter) |
| 103 | { |
| 104 | insertProcessorArgsFragmentMain(snippet, argKey: "/*%QT_ARGS_MAIN%*/" , argListFunc: QSSGMaterialShaderGenerator::shadedFragmentMainArgumentList, materialAdapter, isSharedInout: true); |
| 105 | } |
| 106 | |
| 107 | static inline void insertPostProcessorArgs(QByteArray &snippet, QSSGShaderMaterialAdapter *materialAdapter) |
| 108 | { |
| 109 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_POST_PROCESS%*/" , argListFunc: QSSGMaterialShaderGenerator::postProcessorArgumentList, materialAdapter, isSharedInout: false); |
| 110 | } |
| 111 | |
| 112 | static inline void insertVertexMainArgs(QByteArray &snippet) |
| 113 | { |
| 114 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_MAIN%*/" , argListFunc: QSSGMaterialShaderGenerator::vertexMainArgumentList); |
| 115 | } |
| 116 | |
| 117 | static inline void insertVertexInstancedMainArgs(QByteArray &snippet) |
| 118 | { |
| 119 | insertProcessorArgs(snippet, argKey: "/*%QT_ARGS_MAIN%*/" , argListFunc: QSSGMaterialShaderGenerator::vertexInstancedMainArgumentList); |
| 120 | } |
| 121 | |
| 122 | static inline const char *customMainCallWithArguments(bool usesInstancing) |
| 123 | { |
| 124 | if (usesInstancing) |
| 125 | return " qt_customMain(qt_vertPosition.xyz, qt_vertNormal, qt_vertUV0, qt_vertUV1, qt_vertTangent, qt_vertBinormal, qt_vertJoints, qt_vertWeights, qt_vertColor, qt_instancedModelMatrix, qt_instancedMVPMatrix);" ; |
| 126 | else |
| 127 | return " qt_customMain(qt_vertPosition.xyz, qt_vertNormal, qt_vertUV0, qt_vertUV1, qt_vertTangent, qt_vertBinormal, qt_vertJoints, qt_vertWeights, qt_vertColor);\n" ; |
| 128 | } |
| 129 | |
| 130 | void QSSGMaterialVertexPipeline::beginVertexGeneration(const QSSGShaderDefaultMaterialKey &inKey, |
| 131 | const QSSGShaderFeatures &inFeatureSet, |
| 132 | QSSGShaderLibraryManager &shaderLibraryManager) |
| 133 | { |
| 134 | QSSGShaderGeneratorStageFlags theStages(QSSGProgramGenerator::defaultFlags()); |
| 135 | programGenerator()->beginProgram(inEnabledStages: theStages); |
| 136 | |
| 137 | QSSGStageGeneratorBase &vertexShader(vertex()); |
| 138 | |
| 139 | const bool meshHasNormals = defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue( |
| 140 | bit: QSSGShaderKeyVertexAttribute::Normal, inKeySet: inKey); |
| 141 | const bool meshHasTexCoord0 = defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue( |
| 142 | bit: QSSGShaderKeyVertexAttribute::TexCoord0, inKeySet: inKey); |
| 143 | const bool meshHasTexCoord1 = defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue( |
| 144 | bit: QSSGShaderKeyVertexAttribute::TexCoord1, inKeySet: inKey); |
| 145 | const bool meshHasTexCoordLightmap = defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue( |
| 146 | bit: QSSGShaderKeyVertexAttribute::TexCoordLightmap, inKeySet: inKey); |
| 147 | const bool meshHasTangents = defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue( |
| 148 | bit: QSSGShaderKeyVertexAttribute::Tangent, inKeySet: inKey); |
| 149 | const bool meshHasBinormals = defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue( |
| 150 | bit: QSSGShaderKeyVertexAttribute::Binormal, inKeySet: inKey); |
| 151 | const bool meshHasColors = defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue( |
| 152 | bit: QSSGShaderKeyVertexAttribute::Color, inKeySet: inKey); |
| 153 | const bool meshHasJointsAndWeights = defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue( |
| 154 | bit: QSSGShaderKeyVertexAttribute::JointAndWeight, inKeySet: inKey); |
| 155 | const bool overridesPosition = defaultMaterialShaderKeyProperties.m_overridesPosition.getValue(inDataStore: inKey); |
| 156 | const bool usesProjectionMatrix = defaultMaterialShaderKeyProperties.m_usesProjectionMatrix.getValue(inDataStore: inKey); |
| 157 | const bool usesInvProjectionMatrix = defaultMaterialShaderKeyProperties.m_usesInverseProjectionMatrix.getValue(inDataStore: inKey); |
| 158 | const bool usesPointsTopology = defaultMaterialShaderKeyProperties.m_usesPointsTopology.getValue(inDataStore: inKey); |
| 159 | const bool usesFloatJointIndices = defaultMaterialShaderKeyProperties.m_usesFloatJointIndices.getValue(inDataStore: inKey); |
| 160 | const bool blendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(inDataStore: inKey); |
| 161 | usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey); |
| 162 | m_hasSkinning = defaultMaterialShaderKeyProperties.m_boneCount.getValue(inDataStore: inKey) > 0; |
| 163 | const auto morphSize = defaultMaterialShaderKeyProperties.m_targetCount.getValue(inDataStore: inKey); |
| 164 | m_hasMorphing = morphSize > 0; |
| 165 | m_viewCount = inFeatureSet.isSet(feature: QSSGShaderFeatures::Feature::DisableMultiView) |
| 166 | ? 1 : defaultMaterialShaderKeyProperties.m_viewCount.getValue(inDataStore: inKey); |
| 167 | const bool usesViewIndex = defaultMaterialShaderKeyProperties.m_usesViewIndex.getValue(inDataStore: inKey); |
| 168 | |
| 169 | vertexShader.addIncoming(name: "attr_pos" , type: "vec3" ); |
| 170 | if (usesInstancing) { |
| 171 | vertexShader.addIncoming(name: "qt_instanceTransform0" , type: "vec4" ); |
| 172 | vertexShader.addIncoming(name: "qt_instanceTransform1" , type: "vec4" ); |
| 173 | vertexShader.addIncoming(name: "qt_instanceTransform2" , type: "vec4" ); |
| 174 | vertexShader.addIncoming(name: "qt_instanceColor" , type: "vec4" ); |
| 175 | vertexShader.addIncoming(name: "qt_instanceData" , type: "vec4" ); |
| 176 | } |
| 177 | if (blendParticles) { |
| 178 | vertexShader.addInclude(name: "particles.glsllib" ); |
| 179 | vertexShader.addUniform(name: "qt_particleTexture" , type: "sampler2D" ); |
| 180 | vertexShader.addUniform(name: "qt_countPerSlice" , type: "uint" ); |
| 181 | vertexShader.addUniform(name: "qt_oneOverParticleImageSize" , type: "vec2" ); |
| 182 | vertexShader.addUniform(name: "qt_particleMatrix" , type: "mat4" ); |
| 183 | vertexShader.addUniform(name: "qt_particleIndexOffset" , type: "uint" ); |
| 184 | } |
| 185 | |
| 186 | if (m_hasSkinning && meshHasJointsAndWeights) { |
| 187 | vertexShader.addInclude(name: "skinanim.glsllib" ); |
| 188 | if (usesFloatJointIndices) |
| 189 | vertexShader.addIncoming(name: "attr_joints" , type: "vec4" ); |
| 190 | else |
| 191 | vertexShader.addIncoming(name: "attr_joints" , type: "ivec4" ); |
| 192 | vertexShader.addIncoming(name: "attr_weights" , type: "vec4" ); |
| 193 | |
| 194 | vertexShader.addUniform(name: "qt_boneTexture" , type: "sampler2D" ); |
| 195 | } |
| 196 | if (m_hasMorphing) { |
| 197 | vertexShader.addInclude(name: "morphanim.glsllib" ); |
| 198 | vertexShader.addUniformArray(name: "qt_morphWeights" , type: "float" , size: morphSize); |
| 199 | vertexShader.addUniform(name: "qt_morphTargetTexture" , type: "sampler2DArray" ); |
| 200 | } |
| 201 | |
| 202 | const bool hasCustomVertexShader = materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Vertex); |
| 203 | const bool hasCustomFragmentShader = materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Fragment); |
| 204 | if (hasCustomVertexShader) { |
| 205 | QByteArray snippet = materialAdapter->customShaderSnippet(type: QSSGShaderCache::ShaderType::Vertex, |
| 206 | shaderLibraryManager, |
| 207 | multiViewCompatible: m_viewCount >= 2); |
| 208 | if (materialAdapter->hasCustomShaderFunction(shaderType: QSSGShaderCache::ShaderType::Vertex, |
| 209 | QByteArrayLiteral("qt_customMain" ), |
| 210 | shaderLibraryManager)) |
| 211 | { |
| 212 | if (usesInstancing) |
| 213 | insertVertexInstancedMainArgs(snippet); |
| 214 | else |
| 215 | insertVertexMainArgs(snippet); |
| 216 | |
| 217 | if (materialAdapter->usesCustomSkinning()) { |
| 218 | vertexShader.addInclude(name: "skinanim.glsllib" ); |
| 219 | vertexShader.addUniform(name: "qt_boneTexture" , type: "sampler2D" ); |
| 220 | } |
| 221 | |
| 222 | if (materialAdapter->usesCustomMorphing()) { |
| 223 | vertexShader.addInclude(name: "morphanim_custom.glsllib" ); |
| 224 | if (morphSize > 0) |
| 225 | vertexShader.addUniformArray(name: "qt_morphWeights" , type: "float" , size: morphSize); |
| 226 | vertexShader.addUniform(name: "qt_morphTargetTexture" , type: "sampler2DArray" ); |
| 227 | m_hasMorphing = false; |
| 228 | } |
| 229 | |
| 230 | if (!materialAdapter->isUnshaded()) { |
| 231 | hasCustomShadedMain = true; |
| 232 | } |
| 233 | } |
| 234 | vertexShader << snippet; |
| 235 | } |
| 236 | |
| 237 | vertexShader << "void main()" |
| 238 | << "\n" |
| 239 | << "{" |
| 240 | << "\n" ; |
| 241 | // These local variables will be used for whole the pipeline |
| 242 | // instead of each attributes since it is more convenient |
| 243 | // for adding new routines. |
| 244 | vertexShader.append(data: " vec4 qt_vertPosition = vec4(attr_pos, 1.0);" ); |
| 245 | vertexShader.append(data: " vec3 qt_vertNormal = vec3(0.0);" ); |
| 246 | vertexShader.append(data: " vec3 qt_vertTangent = vec3(0.0);" ); |
| 247 | vertexShader.append(data: " vec3 qt_vertBinormal = vec3(0.0);" ); |
| 248 | if (meshHasTexCoord0 || hasCustomVertexShader) |
| 249 | vertexShader.append(data: " vec2 qt_vertUV0 = vec2(0.0);" ); |
| 250 | if (meshHasTexCoord1 || hasCustomVertexShader) |
| 251 | vertexShader.append(data: " vec2 qt_vertUV1 = vec2(0.0);" ); |
| 252 | if (m_hasSkinning || hasCustomVertexShader) |
| 253 | vertexShader.append(data: " ivec4 qt_vertJoints = ivec4(0);" ); |
| 254 | if (meshHasJointsAndWeights || m_hasSkinning || hasCustomVertexShader) |
| 255 | vertexShader.append(data: " vec4 qt_vertWeights = vec4(0.0);" ); |
| 256 | if (meshHasColors || usesInstancing || blendParticles || hasCustomVertexShader || hasCustomFragmentShader) |
| 257 | vertexShader.append(data: " vec4 qt_vertColor = vec4(1.0);" ); // must be 1,1,1,1 to not alter when multiplying with it |
| 258 | |
| 259 | if (!usesInstancing) { |
| 260 | if (m_viewCount < 2) |
| 261 | vertexShader.addUniform(name: "qt_modelViewProjection" , type: "mat4" ); |
| 262 | else |
| 263 | vertexShader.addUniformArray(name: "qt_modelViewProjection" , type: "mat4" , size: m_viewCount); |
| 264 | } else { |
| 265 | // Must manualy calculate a MVP |
| 266 | vertexShader.addUniform(name: "qt_modelMatrix" , type: "mat4" ); |
| 267 | vertexShader.addUniform(name: "qt_parentMatrix" , type: "mat4" ); |
| 268 | if (m_viewCount < 2) |
| 269 | vertexShader.addUniform(name: "qt_viewProjectionMatrix" , type: "mat4" ); |
| 270 | else |
| 271 | vertexShader.addUniformArray(name: "qt_viewProjectionMatrix" , type: "mat4" , size: m_viewCount); |
| 272 | } |
| 273 | |
| 274 | // The custom fragment main should be skipped if this is a |
| 275 | // depth pass, but not if it is also a OpaqueDepthPrePass |
| 276 | // because then we need to know the real alpha values |
| 277 | skipCustomFragmentSnippet = false; |
| 278 | const bool isDepthPass = inFeatureSet.isSet(feature: QSSGShaderFeatures::Feature::DepthPass); |
| 279 | const bool isOpaqueDepthPrePass = inFeatureSet.isSet(feature: QSSGShaderFeatures::Feature::OpaqueDepthPrePass); |
| 280 | skipCustomFragmentSnippet = (isDepthPass && !isOpaqueDepthPrePass); |
| 281 | |
| 282 | if (hasCustomVertexShader || hasCustomFragmentShader) { |
| 283 | // This is both for unshaded and shaded. Regardless of any other |
| 284 | // condition we have to ensure the keywords (VIEW_MATRIX etc.) promised |
| 285 | // by the documentation are available in *both* the custom vertex and |
| 286 | // fragment shader snippets, even if only one of them is present. |
| 287 | if (m_viewCount < 2) { |
| 288 | vertexShader.addUniform(name: "qt_viewProjectionMatrix" , type: "mat4" ); |
| 289 | vertexShader.addUniform(name: "qt_viewMatrix" , type: "mat4" ); |
| 290 | vertexShader.addUniform(name: "qt_cameraPosition" , type: "vec3" ); |
| 291 | vertexShader.addUniform(name: "qt_cameraDirection" , type: "vec3" ); |
| 292 | if (usesProjectionMatrix) |
| 293 | vertexShader.addUniform(name: "qt_projectionMatrix" , type: "mat4" ); |
| 294 | if (usesInvProjectionMatrix) |
| 295 | vertexShader.addUniform(name: "qt_inverseProjectionMatrix" , type: "mat4" ); |
| 296 | } else { |
| 297 | vertexShader.addUniformArray(name: "qt_viewProjectionMatrix" , type: "mat4" , size: m_viewCount); |
| 298 | vertexShader.addUniformArray(name: "qt_viewMatrix" , type: "mat4" , size: m_viewCount); |
| 299 | vertexShader.addUniformArray(name: "qt_cameraPosition" , type: "vec3" , size: m_viewCount); |
| 300 | vertexShader.addUniformArray(name: "qt_cameraDirection" , type: "vec3" , size: m_viewCount); |
| 301 | if (usesProjectionMatrix) |
| 302 | vertexShader.addUniformArray(name: "qt_projectionMatrix" , type: "mat4" , size: m_viewCount); |
| 303 | if (usesInvProjectionMatrix) |
| 304 | vertexShader.addUniformArray(name: "qt_inverseProjectionMatrix" , type: "mat4" , size: m_viewCount); |
| 305 | } |
| 306 | vertexShader.addUniform(name: "qt_modelMatrix" , type: "mat4" ); |
| 307 | vertexShader.addUniform(name: "qt_normalMatrix" , type: "mat3" ); |
| 308 | vertexShader.addUniform(name: "qt_cameraProperties" , type: "vec2" ); |
| 309 | } |
| 310 | |
| 311 | // With multiview, qt_viewIndex (aka VIEW_INDEX) is always present. |
| 312 | // Otherwise, we still make VIEW_INDEX functional (always 0) in custom |
| 313 | // materials, if the keyword is used. |
| 314 | if (m_viewCount >= 2) { |
| 315 | addFlatParameter(inParamName: "qt_viewIndex" , inParamType: "uint" ); |
| 316 | vertexShader.append(data: " qt_viewIndex = gl_ViewIndex;" ); |
| 317 | } else if (usesViewIndex) { |
| 318 | addFlatParameter(inParamName: "qt_viewIndex" , inParamType: "uint" ); |
| 319 | vertexShader.append(data: " qt_viewIndex = 0;" ); |
| 320 | } |
| 321 | |
| 322 | if (meshHasNormals) { |
| 323 | vertexShader.append(data: " qt_vertNormal = attr_norm;" ); |
| 324 | vertexShader.addIncoming(name: "attr_norm" , type: "vec3" ); |
| 325 | } |
| 326 | if (meshHasTexCoord0) { |
| 327 | vertexShader.append(data: " qt_vertUV0 = attr_uv0;" ); |
| 328 | vertexShader.addIncoming(name: "attr_uv0" , type: "vec2" ); |
| 329 | } |
| 330 | if (meshHasTexCoord1) { |
| 331 | vertexShader.append(data: " qt_vertUV1 = attr_uv1;" ); |
| 332 | vertexShader.addIncoming(name: "attr_uv1" , type: "vec2" ); |
| 333 | } |
| 334 | if (meshHasTexCoordLightmap) { |
| 335 | vertexShader.append(data: " vec2 qt_vertLightmapUV = attr_lightmapuv;" ); |
| 336 | vertexShader.addIncoming(name: "attr_lightmapuv" , type: "vec2" ); |
| 337 | } |
| 338 | if (meshHasTangents) { |
| 339 | vertexShader.append(data: " qt_vertTangent = attr_textan;" ); |
| 340 | vertexShader.addIncoming(name: "attr_textan" , type: "vec3" ); |
| 341 | } |
| 342 | if (meshHasBinormals) { |
| 343 | vertexShader.append(data: " qt_vertBinormal = attr_binormal;" ); |
| 344 | vertexShader.addIncoming(name: "attr_binormal" , type: "vec3" ); |
| 345 | } |
| 346 | if (meshHasColors) { |
| 347 | vertexShader.append(data: " qt_vertColor = attr_color;" ); |
| 348 | vertexShader.addIncoming(name: "attr_color" , type: "vec4" ); |
| 349 | } |
| 350 | |
| 351 | if (meshHasJointsAndWeights && (m_hasSkinning || hasCustomVertexShader)) { |
| 352 | if (usesFloatJointIndices) { |
| 353 | vertexShader.addIncoming(name: "attr_joints" , type: "vec4" ); |
| 354 | vertexShader.append(data: " qt_vertJoints = ivec4(attr_joints);" ); |
| 355 | } else { |
| 356 | vertexShader.addIncoming(name: "attr_joints" , type: "ivec4" ); |
| 357 | vertexShader.append(data: " qt_vertJoints = attr_joints;" ); |
| 358 | } |
| 359 | vertexShader.addIncoming(name: "attr_weights" , type: "vec4" ); |
| 360 | vertexShader.append(data: " qt_vertWeights = attr_weights;" ); |
| 361 | } |
| 362 | |
| 363 | if (usesInstancing) { |
| 364 | vertexShader.append(data: " qt_vertColor *= qt_instanceColor;" ); |
| 365 | vertexShader.append(data: " mat4 qt_instanceMatrix = mat4(qt_instanceTransform0, qt_instanceTransform1, qt_instanceTransform2, vec4(0.0, 0.0, 0.0, 1.0));" ); |
| 366 | if (m_hasSkinning) |
| 367 | vertexShader.append(data: " mat4 qt_instancedModelMatrix = qt_parentMatrix * transpose(qt_instanceMatrix);" ); |
| 368 | else |
| 369 | vertexShader.append(data: " mat4 qt_instancedModelMatrix = qt_parentMatrix * transpose(qt_instanceMatrix) * qt_modelMatrix;" ); |
| 370 | vertexShader.append(data: " mat3 qt_instancedNormalMatrix = mat3(transpose(inverse(qt_instancedModelMatrix)));" ); |
| 371 | if (m_viewCount < 2) |
| 372 | vertexShader.append(data: " mat4 qt_instancedMVPMatrix = qt_viewProjectionMatrix * qt_instancedModelMatrix;" ); |
| 373 | else |
| 374 | vertexShader.append(data: " mat4 qt_instancedMVPMatrix = qt_viewProjectionMatrix[qt_viewIndex] * qt_instancedModelMatrix;" ); |
| 375 | } |
| 376 | |
| 377 | if (!materialAdapter->isUnshaded() || !hasCustomVertexShader) { |
| 378 | vertexShader << " vec3 qt_uTransform;\n" ; |
| 379 | vertexShader << " vec3 qt_vTransform;\n" ; |
| 380 | |
| 381 | if (hasCustomShadedMain) |
| 382 | vertexShader.append(data: customMainCallWithArguments(usesInstancing)); |
| 383 | |
| 384 | if (m_hasMorphing && !hasCustomVertexShader) |
| 385 | vertexShader.append(data: " qt_vertPosition.xyz = qt_getTargetPosition(qt_vertPosition.xyz);" ); |
| 386 | |
| 387 | m_needsSkinning = m_hasSkinning && !materialAdapter->usesCustomSkinning(); |
| 388 | if (m_needsSkinning) { |
| 389 | vertexShader.append(data: " mat4 skinMat = mat4(1);" ); |
| 390 | vertexShader.append(data: " if (qt_vertWeights != vec4(0.0)) {" ); |
| 391 | vertexShader.append(data: " skinMat = qt_getSkinMatrix(qt_vertJoints, qt_vertWeights);" ); |
| 392 | vertexShader.append(data: " qt_vertPosition = skinMat * qt_vertPosition;" ); |
| 393 | vertexShader.append(data: " }" ); |
| 394 | } |
| 395 | if (blendParticles) { |
| 396 | vertexShader.append(data: " qt_vertPosition.xyz = qt_applyParticle(qt_vertPosition.xyz, qt_vertNormal, qt_vertColor, qt_vertNormal, qt_vertColor, qt_particleMatrix);" ); |
| 397 | } |
| 398 | |
| 399 | if (!hasCustomShadedMain || !overridesPosition) { |
| 400 | if (!usesInstancing) { |
| 401 | if (m_viewCount < 2) |
| 402 | vertexShader.append(data: " gl_Position = qt_modelViewProjection * qt_vertPosition;" ); |
| 403 | else |
| 404 | vertexShader.append(data: " gl_Position = qt_modelViewProjection[qt_viewIndex] * qt_vertPosition;" ); |
| 405 | } else { |
| 406 | vertexShader.append(data: " gl_Position = qt_instancedMVPMatrix * qt_vertPosition;" ); |
| 407 | } |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | if (usesPointsTopology && !hasCustomVertexShader) { |
| 412 | vertexShader.addUniform(name: "qt_materialPointSize" , type: "float" ); |
| 413 | vertexShader.append(data: " gl_PointSize = qt_materialPointSize;" ); |
| 414 | } // with a custom vertex shader it is up to it to set gl_PointSize (aka POINT_SIZE) |
| 415 | } |
| 416 | |
| 417 | void QSSGMaterialVertexPipeline::beginFragmentGeneration(QSSGShaderLibraryManager &shaderLibraryManager, QSSGRenderLayer::OITMethod oitMethod) |
| 418 | { |
| 419 | fragment().addUniform(name: "qt_material_properties" , type: "vec4" ); |
| 420 | fragment().addUniform(name: "qt_rhi_properties" , type: "vec4" ); |
| 421 | |
| 422 | if (m_viewCount < 2) { |
| 423 | fragment().addUniform(name: "qt_viewMatrix" , type: "mat4" ); |
| 424 | } else { |
| 425 | fragment().addUniformArray(name: "qt_viewMatrix" , type: "mat4" , size: m_viewCount); |
| 426 | } |
| 427 | |
| 428 | if (!skipCustomFragmentSnippet && materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Fragment)) { |
| 429 | QByteArray snippet = materialAdapter->customShaderSnippet(type: QSSGShaderCache::ShaderType::Fragment, |
| 430 | shaderLibraryManager, |
| 431 | multiViewCompatible: m_viewCount >= 2); |
| 432 | if (!materialAdapter->isUnshaded()) { |
| 433 | insertAmbientLightProcessorArgs(snippet, materialAdapter); |
| 434 | insertIblProbeProcessorArgs(snippet, materialAdapter); |
| 435 | insertSpecularLightProcessorArgs(snippet, materialAdapter); |
| 436 | insertSpotLightProcessorArgs(snippet, materialAdapter); |
| 437 | insertPointLightProcessorArgs(snippet, materialAdapter); |
| 438 | insertDirectionalLightProcessorArgs(snippet, materialAdapter); |
| 439 | insertFragmentMainArgs(snippet, materialAdapter); |
| 440 | insertPostProcessorArgs(snippet, materialAdapter); |
| 441 | } |
| 442 | fragment() << snippet; |
| 443 | } |
| 444 | if (oitMethod != QSSGRenderLayer::OITMethod::None) { |
| 445 | if (oitMethod == QSSGRenderLayer::OITMethod::WeightedBlended) { |
| 446 | fragment().addDefinition(name: "QSSG_OIT_METHOD" , value: "QSSG_OIT_WEIGHTED_BLENDED" ); |
| 447 | fragment() << "layout(location = 1) out vec4 revealageOutput;" << "\n" ; |
| 448 | } |
| 449 | } |
| 450 | fragment() << "void main()" |
| 451 | << "\n" |
| 452 | << "{" |
| 453 | << "\n" ; |
| 454 | |
| 455 | if (!materialAdapter->isUnshaded() || !materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Fragment)) |
| 456 | fragment() << " float qt_objectOpacity = qt_material_properties.a;\n" ; |
| 457 | } |
| 458 | |
| 459 | void QSSGMaterialVertexPipeline::assignOutput(const QByteArray &inVarName, const QByteArray &inVarValue) |
| 460 | { |
| 461 | vertex() << " " << inVarName << " = " << inVarValue << ";\n" ; |
| 462 | } |
| 463 | |
| 464 | void QSSGMaterialVertexPipeline::doGenerateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey) |
| 465 | { |
| 466 | QSSGStageGeneratorBase &vertexGenerator(vertex()); |
| 467 | const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey); |
| 468 | if (!usesInstancing) |
| 469 | vertexGenerator.addUniform(name: "qt_normalMatrix" , type: "mat3" ); |
| 470 | if (m_hasMorphing) |
| 471 | vertexGenerator.append(data: " qt_vertNormal = qt_getTargetNormal(qt_vertNormal);" ); |
| 472 | if (m_hasSkinning) { |
| 473 | vertexGenerator.append(data: " if (qt_vertWeights != vec4(0.0))" ); |
| 474 | vertexGenerator.append(data: " qt_vertNormal = qt_getSkinNormalMatrix(qt_vertJoints, qt_vertWeights) * qt_vertNormal;" ); |
| 475 | } |
| 476 | // If new model->skin is used, |
| 477 | // both qt_normalMatrix and qt_modelMatrix are identity. |
| 478 | if (!usesInstancing) { |
| 479 | if (m_hasSkinning) |
| 480 | vertexGenerator.append(data: " vec3 qt_world_normal = normalize(qt_vertNormal);" ); |
| 481 | else |
| 482 | vertexGenerator.append(data: " vec3 qt_world_normal = normalize(qt_normalMatrix * qt_vertNormal);" ); |
| 483 | } else { |
| 484 | vertexGenerator.append(data: " vec3 qt_world_normal = normalize(qt_instancedNormalMatrix * qt_vertNormal);" ); |
| 485 | } |
| 486 | vertexGenerator.append(data: " qt_varNormal = qt_world_normal;" ); |
| 487 | } |
| 488 | |
| 489 | void QSSGMaterialVertexPipeline::doGenerateVarTangent(const QSSGShaderDefaultMaterialKey &inKey) |
| 490 | { |
| 491 | if (m_hasMorphing) |
| 492 | vertex() << " qt_vertTangent = qt_getTargetTangent(qt_vertTangent);\n" ; |
| 493 | if (m_needsSkinning) { |
| 494 | vertex() << " if (qt_vertWeights != vec4(0.0))\n" |
| 495 | << " qt_vertTangent = (skinMat * vec4(qt_vertTangent, 0.0)).xyz;\n" ; |
| 496 | |
| 497 | } |
| 498 | const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey); |
| 499 | if (!usesInstancing) { |
| 500 | if (!m_hasSkinning) |
| 501 | vertex() << " qt_varTangent = (qt_modelMatrix * vec4(qt_vertTangent, 0.0)).xyz;\n" ; |
| 502 | else |
| 503 | vertex() << " qt_varTangent = qt_vertTangent;\n" ; |
| 504 | } else { |
| 505 | vertex() << " qt_varTangent = (qt_instancedModelMatrix * vec4(qt_vertTangent, 0.0)).xyz;\n" ; |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | void QSSGMaterialVertexPipeline::doGenerateVarBinormal(const QSSGShaderDefaultMaterialKey &inKey) |
| 510 | { |
| 511 | if (m_hasMorphing) |
| 512 | vertex() << " qt_vertBinormal = qt_getTargetBinormal(qt_vertBinormal);\n" ; |
| 513 | if (m_needsSkinning) { |
| 514 | vertex() << " if (qt_vertWeights != vec4(0.0))\n" |
| 515 | << " qt_vertBinormal = (skinMat * vec4(qt_vertBinormal, 0.0)).xyz;\n" ; |
| 516 | } |
| 517 | const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey); |
| 518 | if (!usesInstancing) { |
| 519 | if (!m_hasSkinning) |
| 520 | vertex() << " qt_varBinormal = (qt_modelMatrix * vec4(qt_vertBinormal, 0.0)).xyz;\n" ; |
| 521 | else |
| 522 | vertex() << " qt_varBinormal = qt_vertBinormal;\n" ; |
| 523 | } else { |
| 524 | vertex() << " qt_varBinormal = (qt_instancedModelMatrix * vec4(qt_vertBinormal, 0.0)).xyz;\n" ; |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | bool QSSGMaterialVertexPipeline::hasAttributeInKey(QSSGShaderKeyVertexAttribute::VertexAttributeBits inAttr, |
| 529 | const QSSGShaderDefaultMaterialKey &inKey) |
| 530 | { |
| 531 | return defaultMaterialShaderKeyProperties.m_vertexAttributes.getBitValue(bit: inAttr, inKeySet: inKey); |
| 532 | } |
| 533 | |
| 534 | void QSSGMaterialVertexPipeline::endVertexGeneration() |
| 535 | { |
| 536 | if (materialAdapter->isUnshaded() && materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Vertex)) |
| 537 | vertex() << customMainCallWithArguments(usesInstancing); |
| 538 | vertex().append(data: "}" ); |
| 539 | } |
| 540 | |
| 541 | void QSSGMaterialVertexPipeline::endFragmentGeneration() |
| 542 | { |
| 543 | if (!skipCustomFragmentSnippet && materialAdapter->isUnshaded() && materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Fragment)) |
| 544 | fragment() << " qt_customMain();\n" ; |
| 545 | |
| 546 | fragment().append(data: "}" ); |
| 547 | } |
| 548 | |
| 549 | void QSSGMaterialVertexPipeline::addInterpolationParameter(const QByteArray &inName, const QByteArray &inType) |
| 550 | { |
| 551 | vertex().addOutgoing(name: inName, type: inType); |
| 552 | fragment().addIncoming(name: inName, type: inType); |
| 553 | } |
| 554 | |
| 555 | void QSSGMaterialVertexPipeline::addFlatParameter(const QByteArray &inName, const QByteArray &inType) |
| 556 | { |
| 557 | vertex().addFlatOutgoing(name: inName, type: inType); |
| 558 | fragment().addFlatIncoming(name: inName, type: inType); |
| 559 | } |
| 560 | |
| 561 | QSSGStageGeneratorBase &QSSGMaterialVertexPipeline::activeStage() |
| 562 | { |
| 563 | return vertex(); |
| 564 | } |
| 565 | |
| 566 | QT_END_NAMESPACE |
| 567 | |