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 | /* clang-format off */ |
6 | |
7 | #include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h> |
8 | #include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | QSSGShaderMaterialAdapter::~QSSGShaderMaterialAdapter() = default; |
13 | |
14 | QSSGShaderMaterialAdapter *QSSGShaderMaterialAdapter::create(const QSSGRenderGraphObject &materialNode) |
15 | { |
16 | switch (materialNode.type) { |
17 | case QSSGRenderGraphObject::Type::DefaultMaterial: |
18 | case QSSGRenderGraphObject::Type::PrincipledMaterial: |
19 | case QSSGRenderGraphObject::Type::SpecularGlossyMaterial: |
20 | return new QSSGShaderDefaultMaterialAdapter(static_cast<const QSSGRenderDefaultMaterial &>(materialNode)); |
21 | |
22 | case QSSGRenderGraphObject::Type::CustomMaterial: |
23 | return new QSSGShaderCustomMaterialAdapter(static_cast<const QSSGRenderCustomMaterial &>(materialNode)); |
24 | |
25 | default: |
26 | break; |
27 | } |
28 | |
29 | return nullptr; |
30 | } |
31 | |
32 | bool QSSGShaderMaterialAdapter::isUnshaded() |
33 | { |
34 | return false; |
35 | } |
36 | |
37 | bool QSSGShaderMaterialAdapter::hasCustomShaderSnippet(QSSGShaderCache::ShaderType) |
38 | { |
39 | return false; |
40 | } |
41 | |
42 | QByteArray QSSGShaderMaterialAdapter::customShaderSnippet(QSSGShaderCache::ShaderType, |
43 | QSSGShaderLibraryManager &) |
44 | { |
45 | return QByteArray(); |
46 | } |
47 | |
48 | bool QSSGShaderMaterialAdapter::hasCustomShaderFunction(QSSGShaderCache::ShaderType, |
49 | const QByteArray &, |
50 | QSSGShaderLibraryManager &) |
51 | { |
52 | return false; |
53 | } |
54 | |
55 | void QSSGShaderMaterialAdapter::setCustomPropertyUniforms(char *, |
56 | QSSGRhiShaderPipeline &, |
57 | const QSSGRenderContextInterface &) |
58 | { |
59 | } |
60 | |
61 | bool QSSGShaderMaterialAdapter::usesSharedVariables() |
62 | { |
63 | return false; |
64 | } |
65 | |
66 | |
67 | |
68 | QSSGShaderDefaultMaterialAdapter::QSSGShaderDefaultMaterialAdapter(const QSSGRenderDefaultMaterial &material) |
69 | : m_material(material) |
70 | { |
71 | } |
72 | |
73 | bool QSSGShaderDefaultMaterialAdapter::isPrincipled() |
74 | { |
75 | return m_material.type == QSSGRenderGraphObject::Type::PrincipledMaterial; |
76 | } |
77 | |
78 | bool QSSGShaderDefaultMaterialAdapter::isSpecularGlossy() |
79 | { |
80 | return m_material.type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial; |
81 | } |
82 | |
83 | bool QSSGShaderDefaultMaterialAdapter::isMetalnessEnabled() |
84 | { |
85 | return m_material.isMetalnessEnabled(); |
86 | } |
87 | |
88 | bool QSSGShaderDefaultMaterialAdapter::isSpecularEnabled() |
89 | { |
90 | return m_material.isSpecularEnabled(); |
91 | } |
92 | |
93 | bool QSSGShaderDefaultMaterialAdapter::isVertexColorsEnabled() |
94 | { |
95 | return m_material.isVertexColorsEnabled(); |
96 | } |
97 | |
98 | bool QSSGShaderDefaultMaterialAdapter::isClearcoatEnabled() |
99 | { |
100 | return m_material.isClearcoatEnabled(); |
101 | } |
102 | |
103 | bool QSSGShaderDefaultMaterialAdapter::isTransmissionEnabled() |
104 | { |
105 | return m_material.isTransmissionEnabled(); |
106 | } |
107 | |
108 | bool QSSGShaderDefaultMaterialAdapter::hasLighting() |
109 | { |
110 | return m_material.hasLighting(); |
111 | } |
112 | |
113 | bool QSSGShaderDefaultMaterialAdapter::usesCustomSkinning() |
114 | { |
115 | return false; |
116 | } |
117 | |
118 | bool QSSGShaderDefaultMaterialAdapter::usesCustomMorphing() |
119 | { |
120 | return false; |
121 | } |
122 | |
123 | QSSGRenderDefaultMaterial::MaterialSpecularModel QSSGShaderDefaultMaterialAdapter::specularModel() |
124 | { |
125 | return m_material.specularModel; |
126 | } |
127 | |
128 | QSSGRenderDefaultMaterial::MaterialAlphaMode QSSGShaderDefaultMaterialAdapter::alphaMode() |
129 | { |
130 | return m_material.alphaMode; |
131 | } |
132 | |
133 | QSSGRenderImage *QSSGShaderDefaultMaterialAdapter::iblProbe() |
134 | { |
135 | return m_material.iblProbe; |
136 | } |
137 | |
138 | QVector3D QSSGShaderDefaultMaterialAdapter::emissiveColor() |
139 | { |
140 | return m_material.emissiveColor; |
141 | } |
142 | |
143 | QVector4D QSSGShaderDefaultMaterialAdapter::color() |
144 | { |
145 | return m_material.color; |
146 | } |
147 | |
148 | QVector3D QSSGShaderDefaultMaterialAdapter::specularTint() |
149 | { |
150 | return m_material.specularTint; |
151 | } |
152 | |
153 | float QSSGShaderDefaultMaterialAdapter::ior() |
154 | { |
155 | return m_material.ior; |
156 | } |
157 | |
158 | float QSSGShaderDefaultMaterialAdapter::fresnelPower() |
159 | { |
160 | return m_material.fresnelPower; |
161 | } |
162 | |
163 | float QSSGShaderDefaultMaterialAdapter::metalnessAmount() |
164 | { |
165 | return m_material.metalnessAmount; |
166 | } |
167 | |
168 | float QSSGShaderDefaultMaterialAdapter::specularAmount() |
169 | { |
170 | return m_material.specularAmount; |
171 | } |
172 | |
173 | float QSSGShaderDefaultMaterialAdapter::specularRoughness() |
174 | { |
175 | return m_material.specularRoughness; |
176 | } |
177 | |
178 | float QSSGShaderDefaultMaterialAdapter::bumpAmount() |
179 | { |
180 | return m_material.bumpAmount; |
181 | } |
182 | |
183 | float QSSGShaderDefaultMaterialAdapter::translucentFallOff() |
184 | { |
185 | return m_material.translucentFalloff; |
186 | } |
187 | |
188 | float QSSGShaderDefaultMaterialAdapter::diffuseLightWrap() |
189 | { |
190 | return m_material.diffuseLightWrap; |
191 | } |
192 | |
193 | float QSSGShaderDefaultMaterialAdapter::occlusionAmount() |
194 | { |
195 | return m_material.occlusionAmount; |
196 | } |
197 | |
198 | float QSSGShaderDefaultMaterialAdapter::alphaCutOff() |
199 | { |
200 | return m_material.alphaCutoff; |
201 | } |
202 | |
203 | float QSSGShaderDefaultMaterialAdapter::pointSize() |
204 | { |
205 | return m_material.pointSize; |
206 | } |
207 | |
208 | float QSSGShaderDefaultMaterialAdapter::lineWidth() |
209 | { |
210 | return m_material.lineWidth; |
211 | } |
212 | |
213 | float QSSGShaderDefaultMaterialAdapter::heightAmount() |
214 | { |
215 | return m_material.heightAmount; |
216 | } |
217 | |
218 | float QSSGShaderDefaultMaterialAdapter::minHeightSamples() |
219 | { |
220 | return m_material.minHeightSamples; |
221 | } |
222 | |
223 | float QSSGShaderDefaultMaterialAdapter::maxHeightSamples() |
224 | { |
225 | return m_material.maxHeightSamples; |
226 | } |
227 | |
228 | float QSSGShaderDefaultMaterialAdapter::clearcoatAmount() |
229 | { |
230 | return m_material.clearcoatAmount; |
231 | } |
232 | |
233 | float QSSGShaderDefaultMaterialAdapter::clearcoatRoughnessAmount() |
234 | { |
235 | return m_material.clearcoatRoughnessAmount; |
236 | } |
237 | |
238 | float QSSGShaderDefaultMaterialAdapter::transmissionFactor() |
239 | { |
240 | return m_material.transmissionFactor; |
241 | } |
242 | |
243 | float QSSGShaderDefaultMaterialAdapter::thicknessFactor() |
244 | { |
245 | return m_material.thicknessFactor; |
246 | } |
247 | |
248 | float QSSGShaderDefaultMaterialAdapter::attenuationDistance() |
249 | { |
250 | return m_material.attenuationDistance; |
251 | } |
252 | |
253 | QVector3D QSSGShaderDefaultMaterialAdapter::attenuationColor() |
254 | { |
255 | return m_material.attenuationColor; |
256 | } |
257 | |
258 | QSSGShaderCustomMaterialAdapter::QSSGShaderCustomMaterialAdapter(const QSSGRenderCustomMaterial &material) |
259 | : m_material(material) |
260 | { |
261 | } |
262 | |
263 | // Act like Principled. Lighting is always on, specular, metalness, etc. support should all be enabled. |
264 | // Unlike Principled, the *enabled values do not depend on the metalness or specularAmount values |
265 | // (we cannot tell what those are if they are written in the shader). |
266 | |
267 | bool QSSGShaderCustomMaterialAdapter::isPrincipled() |
268 | { |
269 | return true; |
270 | } |
271 | |
272 | bool QSSGShaderCustomMaterialAdapter::isSpecularGlossy() |
273 | { |
274 | return false; |
275 | } |
276 | |
277 | bool QSSGShaderCustomMaterialAdapter::isMetalnessEnabled() |
278 | { |
279 | return true; |
280 | } |
281 | |
282 | bool QSSGShaderCustomMaterialAdapter::isSpecularEnabled() |
283 | { |
284 | return true; |
285 | } |
286 | |
287 | bool QSSGShaderCustomMaterialAdapter::isVertexColorsEnabled() |
288 | { |
289 | return m_material.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::VarColor); |
290 | } |
291 | |
292 | bool QSSGShaderCustomMaterialAdapter::isClearcoatEnabled() |
293 | { |
294 | // TODO: Expose Clearcoat properties to custom material |
295 | return false; |
296 | } |
297 | |
298 | bool QSSGShaderCustomMaterialAdapter::isTransmissionEnabled() |
299 | { |
300 | // TODO: Expose Transmission to custom material |
301 | return false; |
302 | } |
303 | |
304 | bool QSSGShaderCustomMaterialAdapter::hasLighting() |
305 | { |
306 | return true; |
307 | } |
308 | |
309 | bool QSSGShaderCustomMaterialAdapter::usesCustomSkinning() |
310 | { |
311 | return m_material.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Skinning); |
312 | } |
313 | |
314 | bool QSSGShaderCustomMaterialAdapter::usesCustomMorphing() |
315 | { |
316 | return m_material.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Morphing); |
317 | } |
318 | |
319 | QSSGRenderDefaultMaterial::MaterialSpecularModel QSSGShaderCustomMaterialAdapter::specularModel() |
320 | { |
321 | return QSSGRenderDefaultMaterial::MaterialSpecularModel::Default; |
322 | } |
323 | |
324 | QSSGRenderDefaultMaterial::MaterialAlphaMode QSSGShaderCustomMaterialAdapter::alphaMode() |
325 | { |
326 | return QSSGRenderDefaultMaterial::MaterialAlphaMode::Default; |
327 | } |
328 | |
329 | QSSGRenderImage *QSSGShaderCustomMaterialAdapter::iblProbe() |
330 | { |
331 | return m_material.m_iblProbe; |
332 | } |
333 | |
334 | // The following are the values that get set into uniforms such as |
335 | // qt_material_properties etc. When a custom shader is present, these values |
336 | // are not used at all. However, a CustomMaterial is also valid without a |
337 | // vertex/fragment shader, or with no custom shaders at all. Therefore the |
338 | // values here must match the defaults of PrincipledMaterial, in order to make |
339 | // PrincipledMaterial { } and CustomMaterial { } identical. |
340 | |
341 | QVector3D QSSGShaderCustomMaterialAdapter::emissiveColor() |
342 | { |
343 | return QVector3D(0, 0, 0); |
344 | } |
345 | |
346 | QVector4D QSSGShaderCustomMaterialAdapter::color() |
347 | { |
348 | return QVector4D(1, 1, 1, 1); |
349 | } |
350 | |
351 | QVector3D QSSGShaderCustomMaterialAdapter::specularTint() |
352 | { |
353 | return QVector3D(1, 1, 1); |
354 | } |
355 | |
356 | float QSSGShaderCustomMaterialAdapter::ior() |
357 | { |
358 | return 1.45f; |
359 | } |
360 | |
361 | float QSSGShaderCustomMaterialAdapter::fresnelPower() |
362 | { |
363 | return 0.0f; |
364 | } |
365 | |
366 | float QSSGShaderCustomMaterialAdapter::metalnessAmount() |
367 | { |
368 | return 0.0f; |
369 | } |
370 | |
371 | float QSSGShaderCustomMaterialAdapter::specularAmount() |
372 | { |
373 | return 0.5f; |
374 | } |
375 | |
376 | float QSSGShaderCustomMaterialAdapter::specularRoughness() |
377 | { |
378 | return 0.0f; |
379 | } |
380 | |
381 | float QSSGShaderCustomMaterialAdapter::bumpAmount() |
382 | { |
383 | return 0.0f; |
384 | } |
385 | |
386 | float QSSGShaderCustomMaterialAdapter::translucentFallOff() |
387 | { |
388 | return 0.0f; |
389 | } |
390 | |
391 | float QSSGShaderCustomMaterialAdapter::diffuseLightWrap() |
392 | { |
393 | return 0.0f; |
394 | } |
395 | |
396 | float QSSGShaderCustomMaterialAdapter::occlusionAmount() |
397 | { |
398 | return 1.0f; |
399 | } |
400 | |
401 | float QSSGShaderCustomMaterialAdapter::alphaCutOff() |
402 | { |
403 | return 0.5f; |
404 | } |
405 | |
406 | float QSSGShaderCustomMaterialAdapter::pointSize() |
407 | { |
408 | return 1.0f; |
409 | } |
410 | |
411 | float QSSGShaderCustomMaterialAdapter::lineWidth() |
412 | { |
413 | return m_material.m_lineWidth; |
414 | } |
415 | |
416 | float QSSGShaderCustomMaterialAdapter::heightAmount() |
417 | { |
418 | return 0.0f; |
419 | } |
420 | |
421 | float QSSGShaderCustomMaterialAdapter::minHeightSamples() |
422 | { |
423 | return 0.0f; |
424 | } |
425 | |
426 | float QSSGShaderCustomMaterialAdapter::maxHeightSamples() |
427 | { |
428 | return 0.0f; |
429 | } |
430 | |
431 | float QSSGShaderCustomMaterialAdapter::clearcoatAmount() |
432 | { |
433 | return 0.0f; |
434 | } |
435 | |
436 | float QSSGShaderCustomMaterialAdapter::clearcoatRoughnessAmount() |
437 | { |
438 | return 0.0f; |
439 | } |
440 | |
441 | float QSSGShaderCustomMaterialAdapter::transmissionFactor() |
442 | { |
443 | return 0.0f; |
444 | } |
445 | |
446 | float QSSGShaderCustomMaterialAdapter::thicknessFactor() |
447 | { |
448 | return 0.0f; |
449 | } |
450 | |
451 | float QSSGShaderCustomMaterialAdapter::attenuationDistance() |
452 | { |
453 | return std::numeric_limits<float>::infinity(); |
454 | } |
455 | |
456 | QVector3D QSSGShaderCustomMaterialAdapter::attenuationColor() |
457 | { |
458 | return { 1.0f, 1.0f, 1.0f }; |
459 | } |
460 | |
461 | bool QSSGShaderCustomMaterialAdapter::isUnshaded() |
462 | { |
463 | return m_material.m_shadingMode == QSSGRenderCustomMaterial::ShadingMode::Unshaded; |
464 | } |
465 | |
466 | bool QSSGShaderCustomMaterialAdapter::hasCustomShaderSnippet(QSSGShaderCache::ShaderType type) |
467 | { |
468 | if (type == QSSGShaderCache::ShaderType::Vertex) |
469 | return m_material.m_customShaderPresence.testFlag(flag: QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Vertex); |
470 | |
471 | return m_material.m_customShaderPresence.testFlag(flag: QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Fragment); |
472 | } |
473 | |
474 | QByteArray QSSGShaderCustomMaterialAdapter::customShaderSnippet(QSSGShaderCache::ShaderType type, |
475 | QSSGShaderLibraryManager &shaderLibraryManager) |
476 | { |
477 | if (hasCustomShaderSnippet(type)) |
478 | return shaderLibraryManager.getShaderSource(inShaderPathKey: m_material.m_shaderPathKey, type); |
479 | |
480 | return QByteArray(); |
481 | } |
482 | |
483 | bool QSSGShaderCustomMaterialAdapter::hasCustomShaderFunction(QSSGShaderCache::ShaderType shaderType, |
484 | const QByteArray &funcName, |
485 | QSSGShaderLibraryManager &shaderLibraryManager) |
486 | { |
487 | if (hasCustomShaderSnippet(type: shaderType)) |
488 | return shaderLibraryManager.getShaderMetaData(inShaderPathKey: m_material.m_shaderPathKey, type: shaderType).customFunctions.contains(value: funcName); |
489 | |
490 | return false; |
491 | } |
492 | |
493 | void QSSGShaderCustomMaterialAdapter::setCustomPropertyUniforms(char *ubufData, |
494 | QSSGRhiShaderPipeline &shaderPipeline, |
495 | const QSSGRenderContextInterface &context) |
496 | { |
497 | context.customMaterialSystem()->applyRhiShaderPropertyValues(ubufData, inMaterial: m_material, shaderPipeline); |
498 | } |
499 | |
500 | bool QSSGShaderCustomMaterialAdapter::usesSharedVariables() |
501 | { |
502 | return m_material.m_usesSharedVariables; |
503 | } |
504 | |
505 | namespace { |
506 | |
507 | // Custom material shader substitution table. |
508 | // Must be in sync with the shader generator. |
509 | static const QSSGCustomMaterialVariableSubstitution qssg_var_subst_tab[] = { |
510 | // uniform (block members) |
511 | { .builtin: "MODELVIEWPROJECTION_MATRIX" , .actualName: "qt_modelViewProjection" }, |
512 | { .builtin: "VIEWPROJECTION_MATRIX" , .actualName: "qt_viewProjectionMatrix" }, |
513 | { .builtin: "MODEL_MATRIX" , .actualName: "qt_modelMatrix" }, |
514 | { .builtin: "VIEW_MATRIX" , .actualName: "qt_viewMatrix" }, |
515 | { .builtin: "NORMAL_MATRIX" , .actualName: "qt_normalMatrix" }, |
516 | { .builtin: "BONE_TRANSFORMS" , .actualName: "qt_getTexMatrix" }, |
517 | { .builtin: "BONE_NORMAL_TRANSFORMS" , .actualName: "qt_getTexMatrix" }, |
518 | { .builtin: "PROJECTION_MATRIX" , .actualName: "qt_projectionMatrix" }, |
519 | { .builtin: "INVERSE_PROJECTION_MATRIX" , .actualName: "qt_inverseProjectionMatrix" }, |
520 | { .builtin: "CAMERA_POSITION" , .actualName: "qt_cameraPosition" }, |
521 | { .builtin: "CAMERA_DIRECTION" , .actualName: "qt_cameraDirection" }, |
522 | { .builtin: "CAMERA_PROPERTIES" , .actualName: "qt_cameraProperties" }, |
523 | { .builtin: "FRAMEBUFFER_Y_UP" , .actualName: "qt_rhi_properties.x" }, |
524 | { .builtin: "NDC_Y_UP" , .actualName: "qt_rhi_properties.y" }, |
525 | { .builtin: "NEAR_CLIP_VALUE" , .actualName: "qt_rhi_properties.z" }, |
526 | { .builtin: "IBL_MAXMIPMAP" , .actualName: "qt_lightProbeProperties.y" }, |
527 | { .builtin: "IBL_HORIZON" , .actualName: "qt_lightProbeProperties.z" }, |
528 | { .builtin: "IBL_EXPOSE" , .actualName: "qt_lightProbeProperties.w" }, |
529 | |
530 | // outputs |
531 | { .builtin: "POSITION" , .actualName: "gl_Position" }, |
532 | { .builtin: "FRAGCOLOR" , .actualName: "fragOutput" }, |
533 | { .builtin: "POINT_SIZE" , .actualName: "gl_PointSize" }, |
534 | |
535 | // fragment inputs |
536 | { .builtin: "FRAGCOORD" , .actualName: "gl_FragCoord" }, |
537 | |
538 | // functions |
539 | { .builtin: "DIRECTIONAL_LIGHT" , .actualName: "qt_directionalLightProcessor" }, |
540 | { .builtin: "POINT_LIGHT" , .actualName: "qt_pointLightProcessor" }, |
541 | { .builtin: "SPOT_LIGHT" , .actualName: "qt_spotLightProcessor" }, |
542 | { .builtin: "AMBIENT_LIGHT" , .actualName: "qt_ambientLightProcessor" }, |
543 | { .builtin: "SPECULAR_LIGHT" , .actualName: "qt_specularLightProcessor" }, |
544 | { .builtin: "MAIN" , .actualName: "qt_customMain" }, |
545 | { .builtin: "POST_PROCESS" , .actualName: "qt_customPostProcessor" }, |
546 | { .builtin: "IBL_PROBE" , .actualName: "qt_iblProbeProcessor" }, |
547 | |
548 | // textures |
549 | { .builtin: "SCREEN_TEXTURE" , .actualName: "qt_screenTexture" }, |
550 | { .builtin: "SCREEN_MIP_TEXTURE" , .actualName: "qt_screenTexture" }, // same resource as SCREEN_TEXTURE under the hood |
551 | { .builtin: "DEPTH_TEXTURE" , .actualName: "qt_depthTexture" }, |
552 | { .builtin: "AO_TEXTURE" , .actualName: "qt_aoTexture" }, |
553 | { .builtin: "IBL_TEXTURE" , .actualName: "qt_lightProbe" }, |
554 | { .builtin: "LIGHTMAP" , .actualName: "qt_lightmap" }, |
555 | |
556 | // For shaded only: vertex outputs, for convenience and perf. (only those |
557 | // that are always present when lighting is enabled) The custom vertex main |
558 | // can also calculate on its own and pass them on with VARYING but that's a |
559 | // bit wasteful since we calculate these anyways. |
560 | { .builtin: "VAR_WORLD_NORMAL" , .actualName: "qt_varNormal" }, |
561 | { .builtin: "VAR_WORLD_TANGENT" , .actualName: "qt_varTangent" }, |
562 | { .builtin: "VAR_WORLD_BINORMAL" , .actualName: "qt_varBinormal" }, |
563 | { .builtin: "VAR_WORLD_POSITION" , .actualName: "qt_varWorldPos" }, |
564 | // vertex color is always enabled for custom materials (shaded) |
565 | { .builtin: "VAR_COLOR" , .actualName: "qt_varColor" }, |
566 | |
567 | // effects |
568 | { .builtin: "INPUT" , .actualName: "qt_inputTexture" }, |
569 | { .builtin: "INPUT_UV" , .actualName: "qt_inputUV" }, |
570 | { .builtin: "TEXTURE_UV" , .actualName: "qt_textureUV" }, |
571 | { .builtin: "INPUT_SIZE" , .actualName: "qt_inputSize" }, |
572 | { .builtin: "OUTPUT_SIZE" , .actualName: "qt_outputSize" }, |
573 | { .builtin: "FRAME" , .actualName: "qt_frame_num" }, |
574 | |
575 | // instancing |
576 | { .builtin: "INSTANCE_COLOR" , .actualName: "qt_instanceColor" }, |
577 | { .builtin: "INSTANCE_DATA" , .actualName: "qt_instanceData" }, |
578 | { .builtin: "INSTANCE_INDEX" , .actualName: "gl_InstanceIndex" }, |
579 | |
580 | // morphing |
581 | { .builtin: "MORPH_POSITION" , .actualName: "qt_getTargetPositionFromTargetId" }, |
582 | { .builtin: "MORPH_NORMAL" , .actualName: "qt_getTargetNormalFromTargetId" }, |
583 | { .builtin: "MORPH_TANGENT" , .actualName: "qt_getTargetTangentFromTargetId" }, |
584 | { .builtin: "MORPH_BINORMAL" , .actualName: "qt_getTargetBinormalFromTargetId" }, |
585 | { .builtin: "MORPH_WEIGHTS" , .actualName: "qt_morphWeights" }, |
586 | |
587 | // custom variables |
588 | { .builtin: "SHARED_VARS" , .actualName: "struct QT_SHARED_VARS" } |
589 | }; |
590 | |
591 | // Functions that, if present, get an argument list injected. |
592 | static const QByteArrayView qssg_func_injectarg_tab[] = { |
593 | "DIRECTIONAL_LIGHT" , |
594 | "POINT_LIGHT" , |
595 | "SPOT_LIGHT" , |
596 | "AMBIENT_LIGHT" , |
597 | "SPECULAR_LIGHT" , |
598 | "MAIN" , |
599 | "POST_PROCESS" , |
600 | "IBL_PROBE" |
601 | }; |
602 | |
603 | // This is based on the Qt Quick shader rewriter (with fixes) |
604 | struct Tokenizer { |
605 | enum Token { |
606 | , |
607 | Token_OpenBrace, |
608 | Token_CloseBrace, |
609 | Token_OpenParen, |
610 | Token_CloseParen, |
611 | Token_SemiColon, |
612 | Token_Identifier, |
613 | Token_Macro, |
614 | Token_OpenBraket, |
615 | Token_CloseBraket, |
616 | Token_Unspecified, |
617 | |
618 | Token_EOF |
619 | }; |
620 | |
621 | void initialize(const QByteArray &input); |
622 | Token next(); |
623 | |
624 | const char *stream; |
625 | const char *pos; |
626 | const char *identifier; |
627 | }; |
628 | |
629 | void Tokenizer::initialize(const QByteArray &input) |
630 | { |
631 | stream = input.constData(); |
632 | pos = input; |
633 | identifier = input; |
634 | } |
635 | |
636 | Tokenizer::Token Tokenizer::next() |
637 | { |
638 | while (*pos) { |
639 | char c = *pos++; |
640 | switch (c) { |
641 | case '/': |
642 | if (*pos == '/') { |
643 | // '//' comment |
644 | ++pos; |
645 | while (*pos && *pos != '\n') ++pos; |
646 | return Token_Comment; |
647 | } else if (*pos == '*') { |
648 | // /* */ comment |
649 | ++pos; |
650 | while (*pos && (*pos != '*' || pos[1] != '/')) ++pos; |
651 | if (*pos) pos += 2; |
652 | return Token_Comment; |
653 | } |
654 | return Token_Unspecified; |
655 | |
656 | case '#': { |
657 | while (*pos) { |
658 | if (*pos == '\n') { |
659 | ++pos; |
660 | break; |
661 | } else if (*pos == '\\') { |
662 | ++pos; |
663 | while (*pos && (*pos == ' ' || *pos == '\t')) |
664 | ++pos; |
665 | if (*pos && (*pos == '\n' || (*pos == '\r' && pos[1] == '\n'))) |
666 | pos += 2; |
667 | } else { |
668 | ++pos; |
669 | } |
670 | } |
671 | return Token_Unspecified; |
672 | } |
673 | |
674 | case ';': return Token_SemiColon; |
675 | case '\0': return Token_EOF; |
676 | case '{': return Token_OpenBrace; |
677 | case '}': return Token_CloseBrace; |
678 | case '(': return Token_OpenParen; |
679 | case ')': return Token_CloseParen; |
680 | case '[': return Token_OpenBraket; |
681 | case ']': return Token_CloseBraket; |
682 | |
683 | case ' ': |
684 | case '\n': |
685 | case '\r': break; |
686 | default: |
687 | // Identifier... |
688 | if ((c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || c == '_') { |
689 | identifier = pos - 1; |
690 | while (*pos && ((*pos >= 'a' && *pos <= 'z') |
691 | || (*pos >= 'A' && *pos <= 'Z') |
692 | || *pos == '_' |
693 | || (*pos >= '0' && *pos <= '9'))) { |
694 | ++pos; |
695 | } |
696 | return Token_Identifier; |
697 | } else { |
698 | return Token_Unspecified; |
699 | } |
700 | } |
701 | } |
702 | |
703 | return Token_EOF; |
704 | } |
705 | } // namespace |
706 | |
707 | QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData |
708 | QSSGShaderCustomMaterialAdapter::prepareCustomShader(QByteArray &dst, |
709 | const QByteArray &shaderCode, |
710 | QSSGShaderCache::ShaderType type, |
711 | const StringPairList &baseUniforms, |
712 | const StringPairList &baseInputs, |
713 | const StringPairList &baseOutputs) |
714 | { |
715 | QByteArrayList inputs; |
716 | QByteArrayList outputs; |
717 | |
718 | Tokenizer tok; |
719 | tok.initialize(input: shaderCode); |
720 | |
721 | QSSGCustomShaderMetaData md = {}; |
722 | QByteArray result; |
723 | result.reserve(asize: 1024); |
724 | // If shader debugging is not enabled we reset the line count to make error message |
725 | // when a shader fails more useful. When shader debugging is enabled the whole shader |
726 | // will be printed and not just the user written part, so in that case we do not want |
727 | // to adjust the line numbers. |
728 | // |
729 | // NOTE: This is not perfect, we do expend the custom material and effect shaders, so |
730 | // there cane still be cases where the reported line numbers are slightly off. |
731 | if (!QSSGRhiContext::shaderDebuggingEnabled()) |
732 | result.prepend(s: "#line 1\n" ); |
733 | const char *lastPos = shaderCode.constData(); |
734 | |
735 | int funcFinderState = 0; |
736 | int useJointTexState = -1; |
737 | int useJointNormalTexState = -1; |
738 | QByteArray currentShadedFunc; |
739 | Tokenizer::Token t = tok.next(); |
740 | while (t != Tokenizer::Token_EOF) { |
741 | switch (t) { |
742 | case Tokenizer::Token_Comment: |
743 | break; |
744 | case Tokenizer::Token_Identifier: |
745 | { |
746 | QByteArray id = QByteArray::fromRawData(data: lastPos, size: tok.pos - lastPos); |
747 | if (id.trimmed() == QByteArrayLiteral("VARYING" )) { |
748 | QByteArray vtype; |
749 | QByteArray vname; |
750 | lastPos = tok.pos; |
751 | t = tok.next(); |
752 | while (t != Tokenizer::Token_EOF) { |
753 | QByteArray data = QByteArray::fromRawData(data: lastPos, size: tok.pos - lastPos); |
754 | if (t == Tokenizer::Token_Identifier) { |
755 | if (vtype.isEmpty()) |
756 | vtype = data.trimmed(); |
757 | else if (vname.isEmpty()) |
758 | vname = data.trimmed(); |
759 | } |
760 | if (t == Tokenizer::Token_SemiColon) |
761 | break; |
762 | lastPos = tok.pos; |
763 | t = tok.next(); |
764 | } |
765 | if (type == QSSGShaderCache::ShaderType::Vertex) |
766 | outputs.append(t: vtype + " " + vname); |
767 | else |
768 | inputs.append(t: vtype + " " + vname); |
769 | } else { |
770 | const QByteArray trimmedId = id.trimmed(); |
771 | if (funcFinderState == 0 && trimmedId == QByteArrayLiteral("void" )) { |
772 | funcFinderState += 1; |
773 | } else if (funcFinderState == 1) { |
774 | auto begin = qssg_func_injectarg_tab; |
775 | const auto end = qssg_func_injectarg_tab + (sizeof(qssg_func_injectarg_tab) / sizeof(qssg_func_injectarg_tab[0])); |
776 | const auto foundIt = std::find_if(first: begin, last: end, pred: [trimmedId](const QByteArrayView &entry) { return entry == trimmedId; }); |
777 | if (foundIt != end) { |
778 | currentShadedFunc = trimmedId; |
779 | funcFinderState += 1; |
780 | } |
781 | } else { |
782 | funcFinderState = 0; |
783 | } |
784 | |
785 | if (trimmedId == QByteArrayLiteral("SCREEN_TEXTURE" )) |
786 | md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture; |
787 | else if (trimmedId == QByteArrayLiteral("SCREEN_MIP_TEXTURE" )) |
788 | md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture; |
789 | else if (trimmedId == QByteArrayLiteral("DEPTH_TEXTURE" )) |
790 | md.flags |= QSSGCustomShaderMetaData::UsesDepthTexture; |
791 | else if (trimmedId == QByteArrayLiteral("AO_TEXTURE" )) |
792 | md.flags |= QSSGCustomShaderMetaData::UsesAoTexture; |
793 | else if (trimmedId == QByteArrayLiteral("POSITION" )) |
794 | md.flags |= QSSGCustomShaderMetaData::OverridesPosition; |
795 | else if (trimmedId == QByteArrayLiteral("PROJECTION_MATRIX" )) |
796 | md.flags |= QSSGCustomShaderMetaData::UsesProjectionMatrix; |
797 | else if (trimmedId == QByteArrayLiteral("INVERSE_PROJECTION_MATRIX" )) |
798 | md.flags |= QSSGCustomShaderMetaData::UsesInverseProjectionMatrix; |
799 | else if (trimmedId == QByteArrayLiteral("VAR_COLOR" )) |
800 | md.flags |= QSSGCustomShaderMetaData::UsesVarColor; |
801 | else if (trimmedId == QByteArrayLiteral("SHARED_VARS" )) |
802 | md.flags |= QSSGCustomShaderMetaData::UsesSharedVars; |
803 | else if (trimmedId == QByteArrayLiteral("IBL_ORIENTATION" )) |
804 | md.flags |= QSSGCustomShaderMetaData::UsesIblOrientation; |
805 | else if (trimmedId == QByteArrayLiteral("LIGHTMAP" )) |
806 | md.flags |= QSSGCustomShaderMetaData::UsesLightmap; |
807 | |
808 | for (const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) { |
809 | if (trimmedId == subst.builtin) { |
810 | id.replace(before: subst.builtin, after: subst.actualName); // replace, not assignment, to keep whitespace etc. |
811 | if (trimmedId == QByteArrayLiteral("BONE_TRANSFORMS" )) { |
812 | useJointTexState = 0; |
813 | md.flags |= QSSGCustomShaderMetaData::UsesSkinning; |
814 | } else if (trimmedId == QByteArrayLiteral("BONE_NORMAL_TRANSFORMS" )) { |
815 | useJointNormalTexState = 0; |
816 | md.flags |= QSSGCustomShaderMetaData::UsesSkinning; |
817 | } |
818 | if (trimmedId == QByteArrayLiteral("MORPH_POSITION" ) || |
819 | trimmedId == QByteArrayLiteral("MORPH_NORMAL" ) || |
820 | trimmedId == QByteArrayLiteral("MORPH_TANGENT" ) || |
821 | trimmedId == QByteArrayLiteral("MORPH_BINORMAL" )) |
822 | md.flags |= QSSGCustomShaderMetaData::UsesMorphing; |
823 | break; |
824 | } |
825 | } |
826 | result += id; |
827 | } |
828 | } |
829 | break; |
830 | case Tokenizer::Token_OpenParen: |
831 | result += QByteArray::fromRawData(data: lastPos, size: tok.pos - lastPos); |
832 | if (funcFinderState == 2) { |
833 | result += QByteArrayLiteral("/*%QT_ARGS_" ); |
834 | result += currentShadedFunc; |
835 | result += QByteArrayLiteral("%*/" ); |
836 | for (const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) { |
837 | if (currentShadedFunc == subst.builtin) { |
838 | currentShadedFunc = subst.actualName.toByteArray(); |
839 | break; |
840 | } |
841 | } |
842 | md.customFunctions.insert(value: currentShadedFunc); |
843 | currentShadedFunc.clear(); |
844 | } |
845 | funcFinderState = 0; |
846 | break; |
847 | case Tokenizer::Token_OpenBraket: |
848 | if (useJointTexState == 0) { |
849 | result += QByteArrayLiteral("(2 * (" ); |
850 | ++useJointTexState; |
851 | break; |
852 | } else if (useJointNormalTexState == 0) { |
853 | result += QByteArrayLiteral("(1 + 2 * (" ); |
854 | ++useJointNormalTexState; |
855 | break; |
856 | } |
857 | |
858 | if (useJointTexState >= 0) |
859 | ++useJointTexState; |
860 | else if (useJointNormalTexState >= 0) |
861 | ++useJointNormalTexState; |
862 | result += QByteArrayLiteral("[" ); |
863 | break; |
864 | case Tokenizer::Token_CloseBraket: |
865 | // This implementation will not allow mixed usages of BONE_TRANSFORMS and |
866 | // BONE_NORMAL_TRANSFORMS. |
867 | // For example, BONE_TRANSFORM[int(BONE_NORMAL_TRANFORMS[i][0].x)] |
868 | // cannot be compiled successfully. |
869 | if (useJointTexState <= 0 && useJointNormalTexState <= 0) { |
870 | result += QByteArrayLiteral("]" ); |
871 | break; |
872 | } |
873 | if (useJointTexState > 1) { |
874 | result += QByteArrayLiteral("]" ); |
875 | --useJointTexState; |
876 | break; |
877 | } else if (useJointNormalTexState > 1) { |
878 | result += QByteArrayLiteral("]" ); |
879 | --useJointNormalTexState; |
880 | break; |
881 | } |
882 | result += QByteArrayLiteral("))" ); |
883 | useJointTexState = -1; |
884 | useJointNormalTexState = -1; |
885 | break; |
886 | default: |
887 | result += QByteArray::fromRawData(data: lastPos, size: tok.pos - lastPos); |
888 | break; |
889 | } |
890 | lastPos = tok.pos; |
891 | t = tok.next(); |
892 | } |
893 | |
894 | result += '\n'; |
895 | |
896 | StringPairList allUniforms = baseUniforms; |
897 | if (md.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesScreenTexture) || md.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesScreenMipTexture)) |
898 | allUniforms.append(t: { "sampler2D" , "qt_screenTexture" }); |
899 | if (md.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesDepthTexture)) |
900 | allUniforms.append(t: { "sampler2D" , "qt_depthTexture" }); |
901 | if (md.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesAoTexture)) |
902 | allUniforms.append(t: { "sampler2D" , "qt_aoTexture" }); |
903 | if (md.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesLightmap)) |
904 | allUniforms.append(t: { "sampler2D" , "qt_lightmap" }); |
905 | |
906 | static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"uniforms\": [\n" ; |
907 | static const char *metaEnd = " ]\n}*/\n#endif\n" ; |
908 | dst.append(s: metaStart); |
909 | for (int i = 0, count = allUniforms.size(); i < count; ++i) { |
910 | const auto &typeAndName(allUniforms[i]); |
911 | dst.append(a: " { \"type\": \"" + typeAndName.first + "\", \"name\": \"" + typeAndName.second + "\" }" ); |
912 | if (i < count - 1) |
913 | dst.append(s: "," ); |
914 | dst.append(s: "\n" ); |
915 | } |
916 | dst.append(s: metaEnd); |
917 | |
918 | const char *stageStr = type == QSSGShaderCache::ShaderType::Vertex ? "vertex" : "fragment" ; |
919 | StringPairList allInputs = baseInputs; |
920 | for (const QByteArray &inputTypeAndName : inputs) { |
921 | const QByteArrayList typeAndName = inputTypeAndName.split(sep: ' '); |
922 | if (typeAndName.size() == 2) |
923 | allInputs.append(t: { typeAndName[0].trimmed(), typeAndName[1].trimmed() }); |
924 | } |
925 | if (!allInputs.isEmpty()) { |
926 | static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"inputs\": [\n" ; |
927 | static const char *metaEnd = " ]\n}*/\n#endif\n" ; |
928 | dst.append(s: metaStart); |
929 | for (int i = 0, count = allInputs.size(); i < count; ++i) { |
930 | dst.append(a: " { \"type\": \"" + allInputs[i].first |
931 | + "\", \"name\": \"" + allInputs[i].second |
932 | + "\", \"stage\": \"" + stageStr + "\" }" ); |
933 | if (i < count - 1) |
934 | dst.append(s: "," ); |
935 | dst.append(s: "\n" ); |
936 | } |
937 | dst.append(s: metaEnd); |
938 | } |
939 | |
940 | StringPairList allOutputs = baseOutputs; |
941 | for (const QByteArray &outputTypeAndName : outputs) { |
942 | const QByteArrayList typeAndName = outputTypeAndName.split(sep: ' '); |
943 | if (typeAndName.size() == 2) |
944 | allOutputs.append(t: { typeAndName[0].trimmed(), typeAndName[1].trimmed() }); |
945 | } |
946 | if (!allOutputs.isEmpty()) { |
947 | static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"outputs\": [\n" ; |
948 | static const char *metaEnd = " ]\n}*/\n#endif\n" ; |
949 | dst.append(s: metaStart); |
950 | for (int i = 0, count = allOutputs.size(); i < count; ++i) { |
951 | dst.append(a: " { \"type\": \"" + allOutputs[i].first |
952 | + "\", \"name\": \"" + allOutputs[i].second |
953 | + "\", \"stage\": \"" + stageStr + "\" }" ); |
954 | if (i < count - 1) |
955 | dst.append(s: "," ); |
956 | dst.append(s: "\n" ); |
957 | } |
958 | dst.append(s: metaEnd); |
959 | } |
960 | |
961 | return { result, md }; |
962 | } |
963 | |
964 | QList<QByteArrayView> QtQuick3DEditorHelpers::CustomMaterial::preprocessorVars() |
965 | { |
966 | QList<QByteArrayView> k; |
967 | k.reserve(asize: std::size(qssg_var_subst_tab)); |
968 | for (const auto &v : qssg_var_subst_tab) |
969 | k.push_back(t: v.builtin); |
970 | return k; |
971 | } |
972 | |
973 | QT_END_NAMESPACE |
974 | |
975 | |