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
10QT_BEGIN_NAMESPACE
11
12QSSGShaderMaterialAdapter::~QSSGShaderMaterialAdapter() = default;
13
14QSSGShaderMaterialAdapter *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
32bool QSSGShaderMaterialAdapter::isUnshaded()
33{
34 return false;
35}
36
37bool QSSGShaderMaterialAdapter::hasCustomShaderSnippet(QSSGShaderCache::ShaderType)
38{
39 return false;
40}
41
42QByteArray QSSGShaderMaterialAdapter::customShaderSnippet(QSSGShaderCache::ShaderType,
43 QSSGShaderLibraryManager &)
44{
45 return QByteArray();
46}
47
48bool QSSGShaderMaterialAdapter::hasCustomShaderFunction(QSSGShaderCache::ShaderType,
49 const QByteArray &,
50 QSSGShaderLibraryManager &)
51{
52 return false;
53}
54
55void QSSGShaderMaterialAdapter::setCustomPropertyUniforms(char *,
56 QSSGRhiShaderPipeline &,
57 const QSSGRenderContextInterface &)
58{
59}
60
61bool QSSGShaderMaterialAdapter::usesSharedVariables()
62{
63 return false;
64}
65
66
67
68QSSGShaderDefaultMaterialAdapter::QSSGShaderDefaultMaterialAdapter(const QSSGRenderDefaultMaterial &material)
69 : m_material(material)
70{
71}
72
73bool QSSGShaderDefaultMaterialAdapter::isPrincipled()
74{
75 return m_material.type == QSSGRenderGraphObject::Type::PrincipledMaterial;
76}
77
78bool QSSGShaderDefaultMaterialAdapter::isSpecularGlossy()
79{
80 return m_material.type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial;
81}
82
83bool QSSGShaderDefaultMaterialAdapter::isMetalnessEnabled()
84{
85 return m_material.isMetalnessEnabled();
86}
87
88bool QSSGShaderDefaultMaterialAdapter::isSpecularEnabled()
89{
90 return m_material.isSpecularEnabled();
91}
92
93bool QSSGShaderDefaultMaterialAdapter::isVertexColorsEnabled()
94{
95 return m_material.isVertexColorsEnabled();
96}
97
98bool QSSGShaderDefaultMaterialAdapter::isClearcoatEnabled()
99{
100 return m_material.isClearcoatEnabled();
101}
102
103bool QSSGShaderDefaultMaterialAdapter::isTransmissionEnabled()
104{
105 return m_material.isTransmissionEnabled();
106}
107
108bool QSSGShaderDefaultMaterialAdapter::hasLighting()
109{
110 return m_material.hasLighting();
111}
112
113bool QSSGShaderDefaultMaterialAdapter::usesCustomSkinning()
114{
115 return false;
116}
117
118bool QSSGShaderDefaultMaterialAdapter::usesCustomMorphing()
119{
120 return false;
121}
122
123QSSGRenderDefaultMaterial::MaterialSpecularModel QSSGShaderDefaultMaterialAdapter::specularModel()
124{
125 return m_material.specularModel;
126}
127
128QSSGRenderDefaultMaterial::MaterialAlphaMode QSSGShaderDefaultMaterialAdapter::alphaMode()
129{
130 return m_material.alphaMode;
131}
132
133QSSGRenderImage *QSSGShaderDefaultMaterialAdapter::iblProbe()
134{
135 return m_material.iblProbe;
136}
137
138QVector3D QSSGShaderDefaultMaterialAdapter::emissiveColor()
139{
140 return m_material.emissiveColor;
141}
142
143QVector4D QSSGShaderDefaultMaterialAdapter::color()
144{
145 return m_material.color;
146}
147
148QVector3D QSSGShaderDefaultMaterialAdapter::specularTint()
149{
150 return m_material.specularTint;
151}
152
153float QSSGShaderDefaultMaterialAdapter::ior()
154{
155 return m_material.ior;
156}
157
158float QSSGShaderDefaultMaterialAdapter::fresnelPower()
159{
160 return m_material.fresnelPower;
161}
162
163float QSSGShaderDefaultMaterialAdapter::metalnessAmount()
164{
165 return m_material.metalnessAmount;
166}
167
168float QSSGShaderDefaultMaterialAdapter::specularAmount()
169{
170 return m_material.specularAmount;
171}
172
173float QSSGShaderDefaultMaterialAdapter::specularRoughness()
174{
175 return m_material.specularRoughness;
176}
177
178float QSSGShaderDefaultMaterialAdapter::bumpAmount()
179{
180 return m_material.bumpAmount;
181}
182
183float QSSGShaderDefaultMaterialAdapter::translucentFallOff()
184{
185 return m_material.translucentFalloff;
186}
187
188float QSSGShaderDefaultMaterialAdapter::diffuseLightWrap()
189{
190 return m_material.diffuseLightWrap;
191}
192
193float QSSGShaderDefaultMaterialAdapter::occlusionAmount()
194{
195 return m_material.occlusionAmount;
196}
197
198float QSSGShaderDefaultMaterialAdapter::alphaCutOff()
199{
200 return m_material.alphaCutoff;
201}
202
203float QSSGShaderDefaultMaterialAdapter::pointSize()
204{
205 return m_material.pointSize;
206}
207
208float QSSGShaderDefaultMaterialAdapter::lineWidth()
209{
210 return m_material.lineWidth;
211}
212
213float QSSGShaderDefaultMaterialAdapter::heightAmount()
214{
215 return m_material.heightAmount;
216}
217
218float QSSGShaderDefaultMaterialAdapter::minHeightSamples()
219{
220 return m_material.minHeightSamples;
221}
222
223float QSSGShaderDefaultMaterialAdapter::maxHeightSamples()
224{
225 return m_material.maxHeightSamples;
226}
227
228float QSSGShaderDefaultMaterialAdapter::clearcoatAmount()
229{
230 return m_material.clearcoatAmount;
231}
232
233float QSSGShaderDefaultMaterialAdapter::clearcoatRoughnessAmount()
234{
235 return m_material.clearcoatRoughnessAmount;
236}
237
238float QSSGShaderDefaultMaterialAdapter::transmissionFactor()
239{
240 return m_material.transmissionFactor;
241}
242
243float QSSGShaderDefaultMaterialAdapter::thicknessFactor()
244{
245 return m_material.thicknessFactor;
246}
247
248float QSSGShaderDefaultMaterialAdapter::attenuationDistance()
249{
250 return m_material.attenuationDistance;
251}
252
253QVector3D QSSGShaderDefaultMaterialAdapter::attenuationColor()
254{
255 return m_material.attenuationColor;
256}
257
258QSSGShaderCustomMaterialAdapter::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
267bool QSSGShaderCustomMaterialAdapter::isPrincipled()
268{
269 return true;
270}
271
272bool QSSGShaderCustomMaterialAdapter::isSpecularGlossy()
273{
274 return false;
275}
276
277bool QSSGShaderCustomMaterialAdapter::isMetalnessEnabled()
278{
279 return true;
280}
281
282bool QSSGShaderCustomMaterialAdapter::isSpecularEnabled()
283{
284 return true;
285}
286
287bool QSSGShaderCustomMaterialAdapter::isVertexColorsEnabled()
288{
289 return m_material.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::VarColor);
290}
291
292bool QSSGShaderCustomMaterialAdapter::isClearcoatEnabled()
293{
294 // TODO: Expose Clearcoat properties to custom material
295 return false;
296}
297
298bool QSSGShaderCustomMaterialAdapter::isTransmissionEnabled()
299{
300 // TODO: Expose Transmission to custom material
301 return false;
302}
303
304bool QSSGShaderCustomMaterialAdapter::hasLighting()
305{
306 return true;
307}
308
309bool QSSGShaderCustomMaterialAdapter::usesCustomSkinning()
310{
311 return m_material.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Skinning);
312}
313
314bool QSSGShaderCustomMaterialAdapter::usesCustomMorphing()
315{
316 return m_material.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Morphing);
317}
318
319QSSGRenderDefaultMaterial::MaterialSpecularModel QSSGShaderCustomMaterialAdapter::specularModel()
320{
321 return QSSGRenderDefaultMaterial::MaterialSpecularModel::Default;
322}
323
324QSSGRenderDefaultMaterial::MaterialAlphaMode QSSGShaderCustomMaterialAdapter::alphaMode()
325{
326 return QSSGRenderDefaultMaterial::MaterialAlphaMode::Default;
327}
328
329QSSGRenderImage *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
341QVector3D QSSGShaderCustomMaterialAdapter::emissiveColor()
342{
343 return QVector3D(0, 0, 0);
344}
345
346QVector4D QSSGShaderCustomMaterialAdapter::color()
347{
348 return QVector4D(1, 1, 1, 1);
349}
350
351QVector3D QSSGShaderCustomMaterialAdapter::specularTint()
352{
353 return QVector3D(1, 1, 1);
354}
355
356float QSSGShaderCustomMaterialAdapter::ior()
357{
358 return 1.45f;
359}
360
361float QSSGShaderCustomMaterialAdapter::fresnelPower()
362{
363 return 0.0f;
364}
365
366float QSSGShaderCustomMaterialAdapter::metalnessAmount()
367{
368 return 0.0f;
369}
370
371float QSSGShaderCustomMaterialAdapter::specularAmount()
372{
373 return 0.5f;
374}
375
376float QSSGShaderCustomMaterialAdapter::specularRoughness()
377{
378 return 0.0f;
379}
380
381float QSSGShaderCustomMaterialAdapter::bumpAmount()
382{
383 return 0.0f;
384}
385
386float QSSGShaderCustomMaterialAdapter::translucentFallOff()
387{
388 return 0.0f;
389}
390
391float QSSGShaderCustomMaterialAdapter::diffuseLightWrap()
392{
393 return 0.0f;
394}
395
396float QSSGShaderCustomMaterialAdapter::occlusionAmount()
397{
398 return 1.0f;
399}
400
401float QSSGShaderCustomMaterialAdapter::alphaCutOff()
402{
403 return 0.5f;
404}
405
406float QSSGShaderCustomMaterialAdapter::pointSize()
407{
408 return 1.0f;
409}
410
411float QSSGShaderCustomMaterialAdapter::lineWidth()
412{
413 return m_material.m_lineWidth;
414}
415
416float QSSGShaderCustomMaterialAdapter::heightAmount()
417{
418 return 0.0f;
419}
420
421float QSSGShaderCustomMaterialAdapter::minHeightSamples()
422{
423 return 0.0f;
424}
425
426float QSSGShaderCustomMaterialAdapter::maxHeightSamples()
427{
428 return 0.0f;
429}
430
431float QSSGShaderCustomMaterialAdapter::clearcoatAmount()
432{
433 return 0.0f;
434}
435
436float QSSGShaderCustomMaterialAdapter::clearcoatRoughnessAmount()
437{
438 return 0.0f;
439}
440
441float QSSGShaderCustomMaterialAdapter::transmissionFactor()
442{
443 return 0.0f;
444}
445
446float QSSGShaderCustomMaterialAdapter::thicknessFactor()
447{
448 return 0.0f;
449}
450
451float QSSGShaderCustomMaterialAdapter::attenuationDistance()
452{
453 return std::numeric_limits<float>::infinity();
454}
455
456QVector3D QSSGShaderCustomMaterialAdapter::attenuationColor()
457{
458 return { 1.0f, 1.0f, 1.0f };
459}
460
461bool QSSGShaderCustomMaterialAdapter::isUnshaded()
462{
463 return m_material.m_shadingMode == QSSGRenderCustomMaterial::ShadingMode::Unshaded;
464}
465
466bool 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
474QByteArray 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
483bool 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
493void QSSGShaderCustomMaterialAdapter::setCustomPropertyUniforms(char *ubufData,
494 QSSGRhiShaderPipeline &shaderPipeline,
495 const QSSGRenderContextInterface &context)
496{
497 context.customMaterialSystem()->applyRhiShaderPropertyValues(ubufData, inMaterial: m_material, shaderPipeline);
498}
499
500bool QSSGShaderCustomMaterialAdapter::usesSharedVariables()
501{
502 return m_material.m_usesSharedVariables;
503}
504
505namespace {
506
507// Custom material shader substitution table.
508// Must be in sync with the shader generator.
509static 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.
592static 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)
604struct Tokenizer {
605 enum Token {
606 Token_Comment,
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
629void Tokenizer::initialize(const QByteArray &input)
630{
631 stream = input.constData();
632 pos = input;
633 identifier = input;
634}
635
636Tokenizer::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
707QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData
708QSSGShaderCustomMaterialAdapter::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
964QList<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
973QT_END_NAMESPACE
974
975

source code of qtquick3d/src/runtimerender/qssgshadermaterialadapter.cpp