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 | #ifndef QSSG_VERTEX_PIPELINE_IMPL_H |
6 | #define QSSG_VERTEX_PIPELINE_IMPL_H |
7 | |
8 | // |
9 | // W A R N I N G |
10 | // ------------- |
11 | // |
12 | // This file is not part of the Qt API. It exists purely as an |
13 | // implementation detail. This header file may change from version to |
14 | // version without notice, or even be removed. |
15 | // |
16 | // We mean it. |
17 | // |
18 | |
19 | #include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h> |
20 | #include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h> |
21 | |
22 | #include <QtCore/QSharedPointer> |
23 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | struct QSSGMaterialVertexPipeline |
27 | { |
28 | enum class GenerationFlag |
29 | { |
30 | UVCoords = 1, |
31 | EnvMapReflection = 1 << 1, |
32 | ViewVector = 1 << 2, |
33 | WorldNormal = 1 << 3, |
34 | ObjectNormal = 1 << 4, |
35 | WorldPosition = 1 << 5, |
36 | TangentBinormal = 1 << 6, |
37 | UVCoords1 = 1 << 7, |
38 | VertexColor = 1 << 8, |
39 | PerspDivDepth = 1 << 9, |
40 | PerspDivWorldPos = 1 << 10 |
41 | }; |
42 | |
43 | typedef QHash<QByteArray, QByteArray> TStrTableStrMap; |
44 | typedef TStrTableStrMap::const_iterator TParamIter; |
45 | typedef QFlags<GenerationFlag> GenerationFlags; |
46 | |
47 | QSSGProgramGenerator *m_programGenerator = nullptr; |
48 | QString m_tempString; |
49 | |
50 | GenerationFlags m_generationFlags; |
51 | bool m_hasSkinning; |
52 | bool m_hasMorphing; |
53 | TStrTableStrMap m_interpolationParameters; |
54 | QList<QByteArray> m_addedFunctions; |
55 | |
56 | const QSSGShaderDefaultMaterialKeyProperties &defaultMaterialShaderKeyProperties; |
57 | QSSGShaderMaterialAdapter *materialAdapter; |
58 | bool useFloatJointIndices; |
59 | bool hasCustomShadedMain; |
60 | bool usesInstancing; |
61 | bool skipCustomFragmentSnippet; |
62 | |
63 | QSSGMaterialVertexPipeline(QSSGProgramGenerator &inProgram, |
64 | const QSSGShaderDefaultMaterialKeyProperties &materialProperties, |
65 | QSSGShaderMaterialAdapter *materialAdapter); |
66 | |
67 | ~QSSGMaterialVertexPipeline() = default; |
68 | |
69 | // Trues true if the code was *not* set. |
70 | bool setCode(GenerationFlag inCode) |
71 | { |
72 | if (m_generationFlags & inCode) |
73 | return true; |
74 | m_generationFlags |= inCode; |
75 | return false; |
76 | } |
77 | bool hasCode(GenerationFlag inCode) { return (m_generationFlags & inCode); } |
78 | QSSGProgramGenerator *programGenerator() const { return m_programGenerator; } |
79 | |
80 | QSSGStageGeneratorBase &vertex() |
81 | { |
82 | return *programGenerator()->getStage(inStage: QSSGShaderGeneratorStage::Vertex); |
83 | } |
84 | QSSGStageGeneratorBase &fragment() |
85 | { |
86 | return *programGenerator()->getStage(inStage: QSSGShaderGeneratorStage::Fragment); |
87 | } |
88 | |
89 | /** |
90 | * @brief Generates UV coordinates in shader code |
91 | * |
92 | * @param[in] inUVSet index of UV data set |
93 | * |
94 | * @return no return |
95 | */ |
96 | void generateUVCoords(quint32 inUVSet, const QSSGShaderDefaultMaterialKey &inKey) |
97 | { |
98 | if (inUVSet == 0 && setCode(GenerationFlag::UVCoords)) |
99 | return; |
100 | if (inUVSet == 1 && setCode(GenerationFlag::UVCoords1)) |
101 | return; |
102 | |
103 | const bool meshHasUV0 = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoord0, inKey); |
104 | const bool meshHasUV1 = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoord1, inKey); |
105 | |
106 | Q_ASSERT(inUVSet == 0 || inUVSet == 1); |
107 | |
108 | if (inUVSet == 0) { |
109 | if (hasCustomShadedMain || meshHasUV0) { |
110 | addInterpolationParameter(inParamName: "qt_varTexCoord0" , inParamType: "vec2" ); |
111 | if (m_hasMorphing) |
112 | vertex().append(data: " qt_vertUV0 = qt_getTargetTex0(qt_vertUV0);" ); |
113 | vertex() << " qt_varTexCoord0 = qt_vertUV0;\n" ; |
114 | fragment() <<" vec2 qt_texCoord0 = qt_varTexCoord0;\n" ; |
115 | } else { |
116 | vertex() << " vec2 qt_varTexCoord0 = vec2(0.0);\n" ; |
117 | fragment() << " vec2 qt_texCoord0 = vec2(0.0);\n" ; |
118 | } |
119 | } else if (inUVSet == 1) { |
120 | if (hasCustomShadedMain || meshHasUV1) { |
121 | addInterpolationParameter(inParamName: "qt_varTexCoord1" , inParamType: "vec2" ); |
122 | if (m_hasMorphing) |
123 | vertex().append(data: " qt_vertUV1 = qt_getTargetTex0(qt_vertUV1);" ); |
124 | vertex() << " qt_varTexCoord1 = qt_vertUV1;\n" ; |
125 | fragment() <<" vec2 qt_texCoord1 = qt_varTexCoord1;\n" ; |
126 | } else { |
127 | vertex() << " vec2 qt_varTexCoord1 = vec2(0.0);\n" ; |
128 | fragment() << " vec2 qt_texCoord1 = vec2(0.0);\n" ; |
129 | } |
130 | } |
131 | } |
132 | |
133 | void generateLightmapUVCoords(const QSSGShaderDefaultMaterialKey &inKey) |
134 | { |
135 | if (hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoordLightmap, inKey)) { |
136 | addInterpolationParameter(inParamName: "qt_varTexCoordLightmap" , inParamType: "vec2" ); |
137 | vertex() << " qt_varTexCoordLightmap = qt_vertLightmapUV;\n" ; |
138 | fragment() <<" vec2 qt_texCoordLightmap = qt_varTexCoordLightmap;\n" ; |
139 | } else { |
140 | vertex() << " vec2 qt_varTexCoordLightmap = vec2(0.0);\n" ; |
141 | fragment() << " vec2 qt_texCoordLightmap = vec2(0.0);\n" ; |
142 | } |
143 | } |
144 | |
145 | void generateEnvMapReflection(const QSSGShaderDefaultMaterialKey &inKey) |
146 | { |
147 | if (setCode(GenerationFlag::EnvMapReflection)) |
148 | return; |
149 | |
150 | generateWorldPosition(inKey); |
151 | generateWorldNormal(inKey); |
152 | QSSGStageGeneratorBase &activeGenerator(activeStage()); |
153 | activeGenerator.addInclude(name: "viewProperties.glsllib" ); |
154 | addInterpolationParameter(inParamName: "qt_var_object_to_camera" , inParamType: "vec3" ); |
155 | |
156 | activeGenerator.append(data: " qt_var_object_to_camera = normalize( qt_local_model_world_position - qt_cameraPosition );" ); |
157 | |
158 | // World normal cannot be relied upon in the vertex shader because of bump maps. |
159 | fragment().append(data: " vec3 environment_map_reflection = reflect( " |
160 | "normalize(qt_var_object_to_camera), qt_world_normal.xyz );" ); |
161 | fragment().append(data: " environment_map_reflection *= vec3( 0.5, 0.5, 0 );" ); |
162 | fragment().append(data: " environment_map_reflection += vec3( 0.5, 0.5, 1.0 );" ); |
163 | } |
164 | void generateViewVector(const QSSGShaderDefaultMaterialKey &inKey) |
165 | { |
166 | if (setCode(GenerationFlag::ViewVector)) |
167 | return; |
168 | |
169 | generateWorldPosition(inKey); |
170 | activeStage().addUniform(name: "qt_cameraPosition" , type: "vec3" ); |
171 | |
172 | |
173 | fragment() << " vec3 qt_view_vector = normalize(qt_cameraPosition - qt_varWorldPos);\n" ; |
174 | } |
175 | |
176 | // fragment shader expects varying vertex normal |
177 | // lighting in vertex pipeline expects qt_world_normal |
178 | |
179 | // qt_world_normal in both vert and frag shader |
180 | void generateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey) |
181 | { |
182 | if (setCode(GenerationFlag::WorldNormal)) |
183 | return; |
184 | |
185 | const bool meshHasNormal = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Normal, inKey); |
186 | |
187 | if (hasCustomShadedMain || meshHasNormal) { |
188 | addInterpolationParameter(inParamName: "qt_varNormal" , inParamType: "vec3" ); |
189 | doGenerateWorldNormal(inKey); |
190 | } else { |
191 | generateWorldPosition(inKey); |
192 | fragment().append(data: " vec3 qt_varNormal = cross(dFdx(qt_varWorldPos), dFdy(qt_varWorldPos));" ); |
193 | } |
194 | fragment().append(data: " vec3 qt_world_normal = normalize(qt_varNormal);" ); |
195 | } |
196 | |
197 | void generateObjectNormal() |
198 | { |
199 | if (setCode(GenerationFlag::ObjectNormal)) |
200 | return; |
201 | |
202 | addInterpolationParameter(inParamName: "qt_varObjectNormal" , inParamType: "vec3" ); |
203 | vertex().append(data: " qt_varObjectNormal = qt_vertNormal;" ); |
204 | fragment().append(data: " vec3 object_normal = normalize(qt_varObjectNormal);" ); |
205 | } |
206 | |
207 | void generateWorldPosition(const QSSGShaderDefaultMaterialKey &inKey) |
208 | { |
209 | if (setCode(GenerationFlag::WorldPosition)) |
210 | return; |
211 | |
212 | activeStage().addUniform(name: "qt_modelMatrix" , type: "mat4" ); |
213 | addInterpolationParameter(inParamName: "qt_varWorldPos" , inParamType: "vec3" ); |
214 | const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey); |
215 | if (!usesInstancing) { |
216 | if (m_hasSkinning) |
217 | vertex().append(data: " vec3 qt_local_model_world_position = qt_vertPosition.xyz;" ); |
218 | else |
219 | vertex().append(data: " vec3 qt_local_model_world_position = (qt_modelMatrix * qt_vertPosition).xyz;" ); |
220 | } else { |
221 | vertex().append(data: " vec3 qt_local_model_world_position = (qt_instancedModelMatrix * qt_vertPosition).xyz;" ); |
222 | } |
223 | |
224 | assignOutput(inVarName: "qt_varWorldPos" , inVarValueExpr: "qt_local_model_world_position" ); |
225 | } |
226 | |
227 | void generateDepth() |
228 | { |
229 | if (setCode(GenerationFlag::PerspDivDepth)) |
230 | return; |
231 | |
232 | addInterpolationParameter(inParamName: "qt_varDepth" , inParamType: "float" ); |
233 | vertex().append(data: " qt_varDepth = gl_Position.z / gl_Position.w;" ); |
234 | } |
235 | |
236 | void generateShadowWorldPosition(const QSSGShaderDefaultMaterialKey &inKey) |
237 | { |
238 | if (setCode(GenerationFlag::PerspDivWorldPos)) |
239 | return; |
240 | |
241 | activeStage().addUniform(name: "qt_modelMatrix" , type: "mat4" ); |
242 | addInterpolationParameter(inParamName: "qt_varShadowWorldPos" , inParamType: "vec3" ); |
243 | |
244 | const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey); |
245 | if (!usesInstancing) { |
246 | if (m_hasSkinning) |
247 | vertex().append(data: " vec4 qt_shadow_world_tmp = qt_vertPosition;" ); |
248 | else |
249 | vertex().append(data: " vec4 qt_shadow_world_tmp = qt_modelMatrix * qt_vertPosition;" ); |
250 | } else { |
251 | vertex().append(data: " vec4 qt_shadow_world_tmp = qt_instancedModelMatrix * qt_vertPosition;" ); |
252 | } |
253 | vertex().append(data: " qt_varShadowWorldPos = qt_shadow_world_tmp.xyz / qt_shadow_world_tmp.w;" ); |
254 | } |
255 | |
256 | void generateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &inKey, bool &genTangent, bool &genBinormal) |
257 | { |
258 | if (setCode(GenerationFlag::TangentBinormal)) |
259 | return; |
260 | |
261 | const bool meshHasTangent = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Tangent, inKey); |
262 | const bool meshHasBinormal = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Binormal, inKey); |
263 | |
264 | // I assume that there is no mesh having only binormal without tangent |
265 | // since it is an abnormal case |
266 | if (hasCustomShadedMain || meshHasTangent) { |
267 | addInterpolationParameter(inParamName: "qt_varTangent" , inParamType: "vec3" ); |
268 | doGenerateVarTangent(inKey); |
269 | fragment() << " vec3 qt_tangent = normalize(qt_varTangent);\n" ; |
270 | |
271 | if (hasCustomShadedMain || meshHasBinormal) { |
272 | addInterpolationParameter(inParamName: "qt_varBinormal" , inParamType: "vec3" ); |
273 | doGenerateVarBinormal(inKey); |
274 | fragment() << " vec3 qt_binormal = normalize(qt_varBinormal);\n" ; |
275 | genBinormal = true; |
276 | } else { |
277 | fragment() << " vec3 qt_binormal = vec3(0.0);\n" ; |
278 | } |
279 | genTangent = true; |
280 | } else { |
281 | fragment() << " vec3 qt_tangent = vec3(0.0);\n" |
282 | << " vec3 qt_binormal = vec3(0.0);\n" ; |
283 | } |
284 | } |
285 | void generateVertexColor(const QSSGShaderDefaultMaterialKey &inKey) |
286 | { |
287 | if (setCode(GenerationFlag::VertexColor)) |
288 | return; |
289 | |
290 | const bool meshHasColor = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Color, inKey); |
291 | |
292 | const bool vColorEnabled = defaultMaterialShaderKeyProperties.m_vertexColorsEnabled.getValue(inDataStore: inKey); |
293 | const bool usesVarColor = defaultMaterialShaderKeyProperties.m_usesVarColor.getValue(inDataStore: inKey); |
294 | const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey); |
295 | const bool usesBlendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(inDataStore: inKey); |
296 | if ((vColorEnabled && meshHasColor) || usesInstancing || usesBlendParticles || usesVarColor) { |
297 | addInterpolationParameter(inParamName: "qt_varColor" , inParamType: "vec4" ); |
298 | if (m_hasMorphing) |
299 | vertex().append(data: " qt_vertColor = qt_getTargetColor(qt_vertColor);" ); |
300 | vertex().append(data: " qt_varColor = qt_vertColor;" ); |
301 | fragment().append(data: " vec4 qt_vertColor = qt_varColor;\n" ); |
302 | } else { |
303 | fragment().append(data: " vec4 qt_vertColor = vec4(1.0);\n" ); // must be 1,1,1,1 to not alter when multiplying with it |
304 | } |
305 | } |
306 | |
307 | void addIncoming(const QByteArray &name, const QByteArray &type) { activeStage().addIncoming(name, type); } |
308 | |
309 | void addOutgoing(const QByteArray &name, const QByteArray &type) { addInterpolationParameter(inParamName: name, inParamType: type); } |
310 | |
311 | void addUniform(const QByteArray &name, const QByteArray &type) { activeStage().addUniform(name, type); } |
312 | |
313 | void addUniformArray(const QByteArray &name, const QByteArray &type, quint32 size) { activeStage().addUniformArray(name, type, size); } |
314 | |
315 | void addInclude(const QByteArray &name) { activeStage().addInclude(name); } |
316 | |
317 | void addFunction(const QByteArray &functionName) |
318 | { |
319 | if (!m_addedFunctions.contains(t: functionName)) { |
320 | m_addedFunctions.push_back(t: functionName); |
321 | QByteArray includeName = "func" + functionName + ".glsllib" ; |
322 | addInclude(name: includeName); |
323 | } |
324 | } |
325 | |
326 | void addConstantBuffer(const QByteArray &name, const QByteArray &layout) |
327 | { |
328 | activeStage().addConstantBuffer(name, layout); |
329 | } |
330 | |
331 | void addConstantBufferParam(const QByteArray &cbName, const QByteArray ¶mName, const QByteArray &type) |
332 | { |
333 | activeStage().addConstantBufferParam(cbName, paramName, type); |
334 | } |
335 | |
336 | void addDefinition(const QByteArray &name, const QByteArray &value = QByteArray()) |
337 | { |
338 | activeStage().addDefinition(name, value); |
339 | } |
340 | |
341 | QSSGStageGeneratorBase &operator<<(const QByteArray &data) |
342 | { |
343 | activeStage() << data; |
344 | return activeStage(); |
345 | } |
346 | |
347 | void append(const QByteArray &data) { activeStage().append(data); } |
348 | |
349 | QSSGShaderGeneratorStage stage() const |
350 | { |
351 | return const_cast<QSSGMaterialVertexPipeline *>(this)->activeStage().stage(); |
352 | } |
353 | |
354 | // Responsible for beginning all vertex and fragment generation (void main() { etc). |
355 | void beginVertexGeneration(const QSSGShaderDefaultMaterialKey &inKey, |
356 | const QSSGShaderFeatures &inFeatureSet, |
357 | QSSGShaderLibraryManager &shaderLibraryManager); |
358 | // The fragment shader expects a floating point constant, qt_objectOpacity to be defined |
359 | // post this method. |
360 | void beginFragmentGeneration(QSSGShaderLibraryManager &shaderLibraryManager); |
361 | // Output variables may be mangled in some circumstances so the shader generation system |
362 | // needs an abstraction |
363 | // mechanism around this. |
364 | void assignOutput(const QByteArray &inVarName, const QByteArray &inVarValueExpr); |
365 | |
366 | // responsible for closing all vertex and fragment generation |
367 | void endVertexGeneration(); |
368 | void endFragmentGeneration(); |
369 | |
370 | QSSGStageGeneratorBase &activeStage(); |
371 | void addInterpolationParameter(const QByteArray &inParamName, const QByteArray &inParamType); |
372 | |
373 | void doGenerateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey); |
374 | void doGenerateVarTangent(const QSSGShaderDefaultMaterialKey &inKey); |
375 | void doGenerateVarBinormal(const QSSGShaderDefaultMaterialKey &inKey); |
376 | bool hasAttributeInKey(QSSGShaderKeyVertexAttribute::VertexAttributeBits inAttr, const QSSGShaderDefaultMaterialKey &inKey); |
377 | }; |
378 | |
379 | QT_END_NAMESPACE |
380 | |
381 | #endif |
382 | |