Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgshadermaterialadapter.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
5
6
7/* clang-format off */
8
9#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
12
14
15QSSGShaderMaterialAdapter::~QSSGShaderMaterialAdapter() = default;
16
17QSSGShaderMaterialAdapter *QSSGShaderMaterialAdapter::create(const QSSGRenderGraphObject &materialNode)
18{
19 switch (materialNode.type) {
20 case QSSGRenderGraphObject::Type::DefaultMaterial:
21 case QSSGRenderGraphObject::Type::PrincipledMaterial:
22 case QSSGRenderGraphObject::Type::SpecularGlossyMaterial:
23 return new QSSGShaderDefaultMaterialAdapter(static_cast<const QSSGRenderDefaultMaterial &>(materialNode));
24
25 case QSSGRenderGraphObject::Type::CustomMaterial:
26 return new QSSGShaderCustomMaterialAdapter(static_cast<const QSSGRenderCustomMaterial &>(materialNode));
27
28 default:
29 break;
30 }
31
32 return nullptr;
33}
34
35bool QSSGShaderMaterialAdapter::isUnshaded()
36{
37 return false;
38}
39
40bool QSSGShaderMaterialAdapter::hasCustomShaderSnippet(QSSGShaderCache::ShaderType)
41{
42 return false;
43}
44
45QByteArray QSSGShaderMaterialAdapter::customShaderSnippet(QSSGShaderCache::ShaderType,
46 QSSGShaderLibraryManager &,
47 bool)
48{
49 return QByteArray();
50}
51
52bool QSSGShaderMaterialAdapter::hasCustomShaderFunction(QSSGShaderCache::ShaderType,
53 const QByteArray &,
54 QSSGShaderLibraryManager &)
55{
56 return false;
57}
58
59void QSSGShaderMaterialAdapter::setCustomPropertyUniforms(char *,
60 QSSGRhiShaderPipeline &,
61 const QSSGRenderContextInterface &)
62{
63}
64
65bool QSSGShaderMaterialAdapter::usesSharedVariables()
66{
67 return false;
68}
69
70
71
72QSSGShaderDefaultMaterialAdapter::QSSGShaderDefaultMaterialAdapter(const QSSGRenderDefaultMaterial &material)
73 : m_material(material)
74{
75}
76
77bool QSSGShaderDefaultMaterialAdapter::isPrincipled()
78{
79 return m_material.type == QSSGRenderGraphObject::Type::PrincipledMaterial;
80}
81
82bool QSSGShaderDefaultMaterialAdapter::isSpecularGlossy()
83{
84 return m_material.type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial;
85}
86
87bool QSSGShaderDefaultMaterialAdapter::isMetalnessEnabled()
88{
89 return m_material.isMetalnessEnabled();
90}
91
92bool QSSGShaderDefaultMaterialAdapter::isSpecularEnabled()
93{
94 return m_material.isSpecularEnabled();
95}
96
97bool QSSGShaderDefaultMaterialAdapter::isVertexColorsEnabled()
98{
99 return m_material.isVertexColorsEnabled();
100}
101
102bool QSSGShaderDefaultMaterialAdapter::isVertexColorsMaskEnabled()
103{
104 return m_material.isVertexColorsMaskEnabled();
105}
106
107bool QSSGShaderDefaultMaterialAdapter::isInvertOpacityMapValue()
108{
109 return m_material.isInvertOpacityMapValue();
110}
111
112bool QSSGShaderDefaultMaterialAdapter::isBaseColorSingleChannelEnabled()
113{
114 return m_material.isBaseColorSingleChannelEnabled();
115}
116
117bool QSSGShaderDefaultMaterialAdapter::isSpecularAmountSingleChannelEnabled()
118{
119 return m_material.isSpecularAmountSingleChannelEnabled();
120}
121
122bool QSSGShaderDefaultMaterialAdapter::isEmissiveSingleChannelEnabled()
123{
124 return m_material.isEmissiveSingleChannelEnabled();
125}
126
127bool QSSGShaderDefaultMaterialAdapter::isClearcoatEnabled()
128{
129 return m_material.isClearcoatEnabled();
130}
131
132bool QSSGShaderDefaultMaterialAdapter::isTransmissionEnabled()
133{
134 return m_material.isTransmissionEnabled();
135}
136
137bool QSSGShaderDefaultMaterialAdapter::hasLighting()
138{
139 return m_material.hasLighting();
140}
141
142bool QSSGShaderDefaultMaterialAdapter::usesCustomSkinning()
143{
144 return false;
145}
146
147bool QSSGShaderDefaultMaterialAdapter::usesCustomMorphing()
148{
149 return false;
150}
151
152QSSGRenderDefaultMaterial::MaterialSpecularModel QSSGShaderDefaultMaterialAdapter::specularModel()
153{
154 return m_material.specularModel;
155}
156
157QSSGRenderDefaultMaterial::MaterialAlphaMode QSSGShaderDefaultMaterialAdapter::alphaMode()
158{
159 return m_material.alphaMode;
160}
161
162QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderDefaultMaterialAdapter::vertexColorRedMask()
163{
164 return m_material.vertexColorRedMask;
165}
166
167QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderDefaultMaterialAdapter::vertexColorGreenMask()
168{
169 return m_material.vertexColorGreenMask;
170}
171
172QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderDefaultMaterialAdapter::vertexColorBlueMask()
173{
174 return m_material.vertexColorBlueMask;
175}
176
177QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderDefaultMaterialAdapter::vertexColorAlphaMask()
178{
179 return m_material.vertexColorAlphaMask;
180}
181
182QSSGRenderImage *QSSGShaderDefaultMaterialAdapter::iblProbe()
183{
184 return m_material.iblProbe;
185}
186
187QVector3D QSSGShaderDefaultMaterialAdapter::emissiveColor()
188{
189 return m_material.emissiveColor;
190}
191
192QVector4D QSSGShaderDefaultMaterialAdapter::color()
193{
194 return m_material.color;
195}
196
197QVector3D QSSGShaderDefaultMaterialAdapter::specularTint()
198{
199 return m_material.specularTint;
200}
201
202float QSSGShaderDefaultMaterialAdapter::ior()
203{
204 return m_material.ior;
205}
206
207bool QSSGShaderDefaultMaterialAdapter::isFresnelScaleBiasEnabled()
208{
209 return m_material.fresnelScaleBiasEnabled;
210}
211
212float QSSGShaderDefaultMaterialAdapter::fresnelScale()
213{
214 return m_material.fresnelScale;
215}
216
217float QSSGShaderDefaultMaterialAdapter::fresnelBias()
218{
219 return m_material.fresnelBias;
220}
221
222float QSSGShaderDefaultMaterialAdapter::fresnelPower()
223{
224 return m_material.fresnelPower;
225}
226
227bool QSSGShaderDefaultMaterialAdapter::isClearcoatFresnelScaleBiasEnabled()
228{
229 return m_material.clearcoatFresnelScaleBiasEnabled;
230}
231
232float QSSGShaderDefaultMaterialAdapter::clearcoatFresnelScale()
233{
234 return m_material.clearcoatFresnelScale;
235}
236
237float QSSGShaderDefaultMaterialAdapter::clearcoatFresnelBias()
238{
239 return m_material.clearcoatFresnelBias;
240}
241
242float QSSGShaderDefaultMaterialAdapter::clearcoatFresnelPower()
243{
244 return m_material.clearcoatFresnelPower;
245}
246
247float QSSGShaderDefaultMaterialAdapter::metalnessAmount()
248{
249 return m_material.metalnessAmount;
250}
251
252float QSSGShaderDefaultMaterialAdapter::specularAmount()
253{
254 return m_material.specularAmount;
255}
256
257float QSSGShaderDefaultMaterialAdapter::specularRoughness()
258{
259 return m_material.specularRoughness;
260}
261
262float QSSGShaderDefaultMaterialAdapter::bumpAmount()
263{
264 return m_material.bumpAmount;
265}
266
267float QSSGShaderDefaultMaterialAdapter::translucentFallOff()
268{
269 return m_material.translucentFalloff;
270}
271
272float QSSGShaderDefaultMaterialAdapter::diffuseLightWrap()
273{
274 return m_material.diffuseLightWrap;
275}
276
277float QSSGShaderDefaultMaterialAdapter::occlusionAmount()
278{
279 return m_material.occlusionAmount;
280}
281
282float QSSGShaderDefaultMaterialAdapter::alphaCutOff()
283{
284 return m_material.alphaCutoff;
285}
286
287float QSSGShaderDefaultMaterialAdapter::pointSize()
288{
289 return m_material.pointSize;
290}
291
292float QSSGShaderDefaultMaterialAdapter::lineWidth()
293{
294 return m_material.lineWidth;
295}
296
297float QSSGShaderDefaultMaterialAdapter::heightAmount()
298{
299 return m_material.heightAmount;
300}
301
302float QSSGShaderDefaultMaterialAdapter::minHeightSamples()
303{
304 return m_material.minHeightSamples;
305}
306
307float QSSGShaderDefaultMaterialAdapter::maxHeightSamples()
308{
309 return m_material.maxHeightSamples;
310}
311
312float QSSGShaderDefaultMaterialAdapter::clearcoatAmount()
313{
314 return m_material.clearcoatAmount;
315}
316
317float QSSGShaderDefaultMaterialAdapter::clearcoatRoughnessAmount()
318{
319 return m_material.clearcoatRoughnessAmount;
320}
321
322float QSSGShaderDefaultMaterialAdapter::clearcoatNormalStrength()
323{
324 return m_material.clearcoatNormalStrength;
325}
326
327float QSSGShaderDefaultMaterialAdapter::transmissionFactor()
328{
329 return m_material.transmissionFactor;
330}
331
332float QSSGShaderDefaultMaterialAdapter::thicknessFactor()
333{
334 return m_material.thicknessFactor;
335}
336
337float QSSGShaderDefaultMaterialAdapter::attenuationDistance()
338{
339 return m_material.attenuationDistance;
340}
341
342QVector3D QSSGShaderDefaultMaterialAdapter::attenuationColor()
343{
344 return m_material.attenuationColor;
345}
346
347QSSGShaderCustomMaterialAdapter::QSSGShaderCustomMaterialAdapter(const QSSGRenderCustomMaterial &material)
348 : m_material(material)
349{
350}
351
352// Act like Principled. Lighting is always on, specular, metalness, etc. support should all be enabled.
353// Unlike Principled, the *enabled values do not depend on the metalness or specularAmount values
354// (we cannot tell what those are if they are written in the shader).
355
356bool QSSGShaderCustomMaterialAdapter::isPrincipled()
357{
358 return true;
359}
360
361bool QSSGShaderCustomMaterialAdapter::isSpecularGlossy()
362{
363 return false;
364}
365
366bool QSSGShaderCustomMaterialAdapter::isMetalnessEnabled()
367{
368 return true;
369}
370
371bool QSSGShaderCustomMaterialAdapter::isSpecularEnabled()
372{
373 return true;
374}
375
376bool QSSGShaderCustomMaterialAdapter::isVertexColorsEnabled()
377{
378 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::VarColor);
379}
380
381bool QSSGShaderCustomMaterialAdapter::isVertexColorsMaskEnabled()
382{
383 return false;
384}
385
386bool QSSGShaderCustomMaterialAdapter::isInvertOpacityMapValue()
387{
388 return false;
389}
390
391bool QSSGShaderCustomMaterialAdapter::isBaseColorSingleChannelEnabled()
392{
393 return false;
394}
395
396bool QSSGShaderCustomMaterialAdapter::isSpecularAmountSingleChannelEnabled()
397{
398 return false;
399}
400
401bool QSSGShaderCustomMaterialAdapter::isEmissiveSingleChannelEnabled()
402{
403 return false;
404}
405
406bool QSSGShaderCustomMaterialAdapter::isClearcoatEnabled()
407{
408 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Clearcoat);
409}
410
411bool QSSGShaderCustomMaterialAdapter::isTransmissionEnabled()
412{
413 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Transmission);
414}
415
416bool QSSGShaderCustomMaterialAdapter::hasLighting()
417{
418 return true;
419}
420
421bool QSSGShaderCustomMaterialAdapter::usesCustomSkinning()
422{
423 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Skinning);
424}
425
426bool QSSGShaderCustomMaterialAdapter::usesCustomMorphing()
427{
428 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Morphing);
429}
430
431QSSGRenderDefaultMaterial::MaterialSpecularModel QSSGShaderCustomMaterialAdapter::specularModel()
432{
433 return QSSGRenderDefaultMaterial::MaterialSpecularModel::SchlickGGX;
434}
435
436QSSGRenderDefaultMaterial::MaterialAlphaMode QSSGShaderCustomMaterialAdapter::alphaMode()
437{
438 return QSSGRenderDefaultMaterial::MaterialAlphaMode::Default;
439}
440
441QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderCustomMaterialAdapter::vertexColorRedMask()
442{
443 return QSSGRenderDefaultMaterial::NoMask;
444}
445
446QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderCustomMaterialAdapter::vertexColorGreenMask()
447{
448 return QSSGRenderDefaultMaterial::NoMask;
449}
450
451QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderCustomMaterialAdapter::vertexColorBlueMask()
452{
453 return QSSGRenderDefaultMaterial::NoMask;
454}
455
456QSSGRenderDefaultMaterial::VertexColorMaskFlags QSSGShaderCustomMaterialAdapter::vertexColorAlphaMask()
457{
458 return QSSGRenderDefaultMaterial::NoMask;
459}
460
461QSSGRenderImage *QSSGShaderCustomMaterialAdapter::iblProbe()
462{
463 return m_material.m_iblProbe;
464}
465
466// The following are the values that get set into uniforms such as
467// qt_material_properties etc. When a custom shader is present, these values
468// are not used at all. However, a CustomMaterial is also valid without a
469// vertex/fragment shader, or with no custom shaders at all. Therefore the
470// values here must match the defaults of PrincipledMaterial, in order to make
471// PrincipledMaterial { } and CustomMaterial { } identical.
472
473QVector3D QSSGShaderCustomMaterialAdapter::emissiveColor()
474{
475 return QVector3D(0, 0, 0);
476}
477
478QVector4D QSSGShaderCustomMaterialAdapter::color()
479{
480 return QVector4D(1, 1, 1, 1);
481}
482
483QVector3D QSSGShaderCustomMaterialAdapter::specularTint()
484{
485 return QVector3D(1, 1, 1);
486}
487
488float QSSGShaderCustomMaterialAdapter::ior()
489{
490 return 1.45f;
491}
492
493bool QSSGShaderCustomMaterialAdapter::isFresnelScaleBiasEnabled()
494{
495 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::FresnelScaleBias);
496}
497
498float QSSGShaderCustomMaterialAdapter::fresnelScale()
499{
500 return 1.0f;
501}
502
503float QSSGShaderCustomMaterialAdapter::fresnelBias()
504{
505 return 0.0f;
506}
507
508float QSSGShaderCustomMaterialAdapter::fresnelPower()
509{
510 return 0.0f;
511}
512
513bool QSSGShaderCustomMaterialAdapter::isClearcoatFresnelScaleBiasEnabled()
514{
515 return m_material.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ClearcoatFresnelScaleBias);
516}
517
518float QSSGShaderCustomMaterialAdapter::clearcoatFresnelScale()
519{
520 return 1.0f;
521}
522
523float QSSGShaderCustomMaterialAdapter::clearcoatFresnelBias()
524{
525 return 0.0f;
526}
527
528float QSSGShaderCustomMaterialAdapter::clearcoatFresnelPower()
529{
530 return 0.0f;
531}
532
533float QSSGShaderCustomMaterialAdapter::metalnessAmount()
534{
535 return 0.0f;
536}
537
538float QSSGShaderCustomMaterialAdapter::specularAmount()
539{
540 return 0.5f;
541}
542
543float QSSGShaderCustomMaterialAdapter::specularRoughness()
544{
545 return 0.0f;
546}
547
548float QSSGShaderCustomMaterialAdapter::bumpAmount()
549{
550 return 0.0f;
551}
552
553float QSSGShaderCustomMaterialAdapter::translucentFallOff()
554{
555 return 0.0f;
556}
557
558float QSSGShaderCustomMaterialAdapter::diffuseLightWrap()
559{
560 return 0.0f;
561}
562
563float QSSGShaderCustomMaterialAdapter::occlusionAmount()
564{
565 return 1.0f;
566}
567
568float QSSGShaderCustomMaterialAdapter::alphaCutOff()
569{
570 return 0.5f;
571}
572
573float QSSGShaderCustomMaterialAdapter::pointSize()
574{
575 return 1.0f;
576}
577
578float QSSGShaderCustomMaterialAdapter::lineWidth()
579{
580 return m_material.m_lineWidth;
581}
582
583float QSSGShaderCustomMaterialAdapter::heightAmount()
584{
585 return 0.0f;
586}
587
588float QSSGShaderCustomMaterialAdapter::minHeightSamples()
589{
590 return 0.0f;
591}
592
593float QSSGShaderCustomMaterialAdapter::maxHeightSamples()
594{
595 return 0.0f;
596}
597
598float QSSGShaderCustomMaterialAdapter::clearcoatAmount()
599{
600 return 0.0f;
601}
602
603float QSSGShaderCustomMaterialAdapter::clearcoatRoughnessAmount()
604{
605 return 0.0f;
606}
607
608float QSSGShaderCustomMaterialAdapter::clearcoatNormalStrength()
609{
610 return 1.0f;
611}
612
613float QSSGShaderCustomMaterialAdapter::transmissionFactor()
614{
615 return 0.0f;
616}
617
618float QSSGShaderCustomMaterialAdapter::thicknessFactor()
619{
620 return 0.0f;
621}
622
623float QSSGShaderCustomMaterialAdapter::attenuationDistance()
624{
625 return std::numeric_limits<float>::infinity();
626}
627
628QVector3D QSSGShaderCustomMaterialAdapter::attenuationColor()
629{
630 return { 1.0f, 1.0f, 1.0f };
631}
632
633bool QSSGShaderCustomMaterialAdapter::isUnshaded()
634{
635 return m_material.m_shadingMode == QSSGRenderCustomMaterial::ShadingMode::Unshaded;
636}
637
638bool QSSGShaderCustomMaterialAdapter::hasCustomShaderSnippet(QSSGShaderCache::ShaderType type)
639{
640 if (type == QSSGShaderCache::ShaderType::Vertex)
641 return m_material.m_customShaderPresence.testFlag(QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Vertex);
642
643 return m_material.m_customShaderPresence.testFlag(QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Fragment);
644}
645
646QByteArray QSSGShaderCustomMaterialAdapter::customShaderSnippet(QSSGShaderCache::ShaderType type,
647 QSSGShaderLibraryManager &shaderLibraryManager,
648 bool multiViewCompatible)
649{
650 if (hasCustomShaderSnippet(type)) {
651 const QByteArray shaderPathKey = m_material.m_shaderPathKey[multiViewCompatible ? QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex
652 : QSSGRenderCustomMaterial::RegularShaderPathKeyIndex];
653 return shaderLibraryManager.getShaderSource(shaderPathKey, type);
654 }
655
656 return QByteArray();
657}
658
659bool QSSGShaderCustomMaterialAdapter::hasCustomShaderFunction(QSSGShaderCache::ShaderType shaderType,
660 const QByteArray &funcName,
661 QSSGShaderLibraryManager &shaderLibraryManager)
662{
663 if (hasCustomShaderSnippet(shaderType))
664 return shaderLibraryManager.getShaderMetaData(m_material.m_shaderPathKey[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex],
665 shaderType).customFunctions.contains(funcName);
666
667 return false;
668}
669
670void QSSGShaderCustomMaterialAdapter::setCustomPropertyUniforms(char *ubufData,
671 QSSGRhiShaderPipeline &shaderPipeline,
672 const QSSGRenderContextInterface &context)
673{
674 context.customMaterialSystem()->applyRhiShaderPropertyValues(ubufData, m_material, shaderPipeline);
675}
676
677bool QSSGShaderCustomMaterialAdapter::usesSharedVariables()
678{
679 return m_material.m_usesSharedVariables;
680}
681
682namespace {
683
684// Custom material shader substitution table.
685// Must be in sync with the shader generator.
686static const QSSGCustomMaterialVariableSubstitution qssg_var_subst_tab[] = {
687 // uniform (block members)
688 { "MODELVIEWPROJECTION_MATRIX", "qt_modelViewProjection", true },
689 { "VIEWPROJECTION_MATRIX", "qt_viewProjectionMatrix", true },
690 { "MODEL_MATRIX", "qt_modelMatrix", false },
691 { "VIEW_MATRIX", "qt_viewMatrix", true },
692 { "NORMAL_MATRIX", "qt_normalMatrix", false },
693 { "BONE_TRANSFORMS", "qt_getTexMatrix", false },
694 { "BONE_NORMAL_TRANSFORMS", "qt_getTexMatrix", false },
695 { "PROJECTION_MATRIX", "qt_projectionMatrix", true },
696 { "INVERSE_PROJECTION_MATRIX", "qt_inverseProjectionMatrix", true },
697 { "CAMERA_POSITION", "qt_cameraPosition", true },
698 { "CAMERA_DIRECTION", "qt_cameraDirection", true },
699 { "CAMERA_PROPERTIES", "qt_cameraProperties", false },
700 { "FRAMEBUFFER_Y_UP", "qt_rhi_properties.x", false },
701 { "NDC_Y_UP", "qt_rhi_properties.y", false },
702 { "NEAR_CLIP_VALUE", "qt_rhi_properties.z", false },
703 { "IBL_MAXMIPMAP", "qt_lightProbeProperties.y", false },
704 { "IBL_HORIZON", "qt_lightProbeProperties.z", false },
705 { "IBL_EXPOSE", "qt_lightProbeProperties.w", false },
706
707 // outputs
708 { "POSITION", "gl_Position", false },
709 { "FRAGCOLOR", "fragOutput", false },
710 { "POINT_SIZE", "gl_PointSize", false },
711
712 // fragment inputs
713 { "FRAGCOORD", "gl_FragCoord", false },
714
715 // functions
716 { "DIRECTIONAL_LIGHT", "qt_directionalLightProcessor", false },
717 { "POINT_LIGHT", "qt_pointLightProcessor", false },
718 { "SPOT_LIGHT", "qt_spotLightProcessor", false },
719 { "AMBIENT_LIGHT", "qt_ambientLightProcessor", false },
720 { "SPECULAR_LIGHT", "qt_specularLightProcessor", false },
721 { "MAIN", "qt_customMain", false },
722 { "POST_PROCESS", "qt_customPostProcessor", false },
723 { "IBL_PROBE", "qt_iblProbeProcessor", false },
724
725 // textures
726 { "SCREEN_TEXTURE", "qt_screenTexture", true },
727 { "SCREEN_MIP_TEXTURE", "qt_screenTexture", true }, // same resource as SCREEN_TEXTURE under the hood
728 { "DEPTH_TEXTURE", "qt_depthTexture", true },
729 { "AO_TEXTURE", "qt_aoTexture", true },
730 { "IBL_TEXTURE", "qt_lightProbe", false },
731 { "LIGHTMAP", "qt_lightmap", false },
732 { "NORMAL_ROUGHNESS_TEXTURE", "qt_normalTexture", false },
733 { "MOTION_VECTOR_TEXTURE", "qt_motionVectorTexture", false },
734
735 // For shaded only: vertex outputs, for convenience and perf. (only those
736 // that are always present when lighting is enabled) The custom vertex main
737 // can also calculate on its own and pass them on with VARYING but that's a
738 // bit wasteful since we calculate these anyways.
739 { "VAR_WORLD_NORMAL", "qt_varNormal", false },
740 { "VAR_WORLD_TANGENT", "qt_varTangent", false },
741 { "VAR_WORLD_BINORMAL", "qt_varBinormal", false },
742 { "VAR_WORLD_POSITION", "qt_varWorldPos", false },
743 // vertex color is always enabled for custom materials (shaded)
744 { "VAR_COLOR", "qt_varColor", false },
745
746 // effects
747 { "INPUT", "qt_inputTexture", true },
748 { "INPUT_UV", "qt_inputUV", false },
749 { "TEXTURE_UV", "qt_textureUV", false },
750 { "INPUT_SIZE", "qt_inputSize", false },
751 { "OUTPUT_SIZE", "qt_outputSize", false },
752 { "FRAME", "qt_frame_num", false },
753
754 // instancing
755 { "INSTANCE_COLOR", "qt_instanceColor", false },
756 { "INSTANCE_DATA", "qt_instanceData", false },
757 { "INSTANCE_INDEX", "gl_InstanceIndex", false },
758
759 // morphing
760 { "MORPH_POSITION", "qt_getTargetPositionFromTargetId", false },
761 { "MORPH_NORMAL", "qt_getTargetNormalFromTargetId", false },
762 { "MORPH_TANGENT", "qt_getTargetTangentFromTargetId", false },
763 { "MORPH_BINORMAL", "qt_getTargetBinormalFromTargetId", false },
764 { "MORPH_WEIGHTS", "qt_morphWeights", false },
765
766 // custom variables
767 { "SHARED_VARS", "struct QT_SHARED_VARS", false },
768
769 // multiview
770 { "VIEW_INDEX", "qt_viewIndex", false }
771};
772
773// Functions that, if present, get an argument list injected.
774static const QByteArrayView qssg_func_injectarg_tab[] = {
775 "DIRECTIONAL_LIGHT",
776 "POINT_LIGHT",
777 "SPOT_LIGHT",
778 "AMBIENT_LIGHT",
779 "SPECULAR_LIGHT",
780 "MAIN",
781 "POST_PROCESS",
782 "IBL_PROBE"
783};
784
785// This is based on the Qt Quick shader rewriter (with fixes)
786struct Tokenizer {
787 enum Token {
788 Token_Comment,
789 Token_OpenBrace,
790 Token_CloseBrace,
791 Token_OpenParen,
792 Token_CloseParen,
793 Token_SemiColon,
794 Token_Identifier,
795 Token_OpenBraket,
796 Token_CloseBraket,
797 Token_Unspecified,
798
799 Token_EOF
800 };
801
802 void initialize(const QByteArray &input);
803 Token next();
804
805 const char *stream;
806 const char *pos;
807 const char *identifier;
808};
809
810void Tokenizer::initialize(const QByteArray &input)
811{
812 stream = input.constData();
813 pos = input;
814 identifier = input;
815}
816
817Tokenizer::Token Tokenizer::next()
818{
819 while (*pos) {
820 char c = *pos++;
821 switch (c) {
822 case '/':
823 if (*pos == '/') {
824 // '//' comment
825 ++pos;
826 while (*pos && *pos != '\n') ++pos;
827 return Token_Comment;
828 } else if (*pos == '*') {
829 // /* */ comment
830 ++pos;
831 while (*pos && (*pos != '*' || pos[1] != '/')) ++pos;
832 if (*pos) pos += 2;
833 return Token_Comment;
834 }
835 return Token_Unspecified;
836
837 case ';': return Token_SemiColon;
838 case '\0': return Token_EOF;
839 case '{': return Token_OpenBrace;
840 case '}': return Token_CloseBrace;
841 case '(': return Token_OpenParen;
842 case ')': return Token_CloseParen;
843 case '[': return Token_OpenBraket;
844 case ']': return Token_CloseBraket;
845
846 case ' ':
847 case '\n':
848 case '\r': break;
849 default:
850 // Identifier...
851 if ((c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || c == '_') {
852 identifier = pos - 1;
853 while (*pos && ((*pos >= 'a' && *pos <= 'z')
854 || (*pos >= 'A' && *pos <= 'Z')
855 || *pos == '_'
856 || (*pos >= '0' && *pos <= '9'))) {
857 ++pos;
858 }
859 return Token_Identifier;
860 } else {
861 return Token_Unspecified;
862 }
863 }
864 }
865
866 return Token_EOF;
867}
868} // namespace
869
870void QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(
871 CustomShaderPrepWorkData *workData,
872 ShaderCodeAndMetaData *codeAndMetaData,
873 const QByteArray &shaderCode,
874 QSSGShaderCache::ShaderType type,
875 bool multiViewCompatible)
876{
877 QByteArrayList inputs;
878 QByteArrayList outputs;
879
880 Tokenizer tok;
881 tok.initialize(shaderCode);
882
883 QSSGCustomShaderMetaData md = {};
884 QByteArray result;
885 result.reserve(1024);
886 // If shader debugging is not enabled we reset the line count to make error message
887 // when a shader fails more useful. When shader debugging is enabled the whole shader
888 // will be printed and not just the user written part, so in that case we do not want
889 // to adjust the line numbers.
890 //
891 // NOTE: This is not perfect, we do expend the custom material and effect shaders, so
892 // there cane still be cases where the reported line numbers are slightly off.
893 if (!QSSGRhiContextPrivate::shaderDebuggingEnabled())
894 result.prepend("#line 1\n");
895 const char *lastPos = shaderCode.constData();
896
897 int funcFinderState = 0;
898 int useJointTexState = -1;
899 int useJointNormalTexState = -1;
900 QByteArray currentShadedFunc;
901 Tokenizer::Token t = tok.next();
902 while (t != Tokenizer::Token_EOF) {
903 switch (t) {
904 case Tokenizer::Token_Comment:
905 break;
906 case Tokenizer::Token_Identifier:
907 {
908 QByteArray id = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
909 if (id.trimmed() == QByteArrayLiteral("VARYING")) {
910 QByteArray vtype;
911 QByteArray vname;
912 bool vflat = false;
913 lastPos = tok.pos;
914 t = tok.next();
915 while (t != Tokenizer::Token_EOF) {
916 QByteArray data = QByteArray::fromRawData(lastPos, tok.pos - lastPos);
917 if (t == Tokenizer::Token_Identifier) {
918 if (vtype.isEmpty()) {
919 vtype = data.trimmed();
920 if (vtype == QByteArrayLiteral("flat")) {
921 vflat = true;
922 vtype.clear();
923 }
924 } else if (vname.isEmpty()) {
925 vname = data.trimmed();
926 }
927 }
928 if (t == Tokenizer::Token_SemiColon)
929 break;
930 lastPos = tok.pos;
931 t = tok.next();
932 }
933 if (type == QSSGShaderCache::ShaderType::Vertex)
934 outputs.append((vflat ? "flat " : "") + vtype + " " + vname);
935 else
936 inputs.append((vflat ? "flat " : "") + vtype + " " + vname);
937 } else {
938 const QByteArray trimmedId = id.trimmed();
939 if (funcFinderState == 0 && trimmedId == QByteArrayLiteral("void")) {
940 funcFinderState += 1;
941 } else if (funcFinderState == 1) {
942 auto begin = qssg_func_injectarg_tab;
943 const auto end = qssg_func_injectarg_tab + (sizeof(qssg_func_injectarg_tab) / sizeof(qssg_func_injectarg_tab[0]));
944 const auto foundIt = std::find_if(begin, end, [trimmedId](const QByteArrayView &entry) { return entry == trimmedId; });
945 if (foundIt != end) {
946 currentShadedFunc = trimmedId;
947 funcFinderState += 1;
948 }
949 } else {
950 funcFinderState = 0;
951 }
952
953 if (trimmedId == QByteArrayLiteral("SCREEN_TEXTURE"))
954 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
955 else if (trimmedId == QByteArrayLiteral("SCREEN_MIP_TEXTURE"))
956 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
957 else if (trimmedId == QByteArrayLiteral("DEPTH_TEXTURE"))
958 md.flags |= QSSGCustomShaderMetaData::UsesDepthTexture;
959 else if (trimmedId == QByteArrayLiteral("NORMAL_ROUGHNESS_TEXTURE"))
960 md.flags |= QSSGCustomShaderMetaData::UsesNormalTexture;
961 else if (trimmedId == QByteArrayLiteral("AO_TEXTURE"))
962 md.flags |= QSSGCustomShaderMetaData::UsesAoTexture;
963 else if (trimmedId == QByteArrayLiteral("POSITION"))
964 md.flags |= QSSGCustomShaderMetaData::OverridesPosition;
965 else if (trimmedId == QByteArrayLiteral("PROJECTION_MATRIX"))
966 md.flags |= QSSGCustomShaderMetaData::UsesProjectionMatrix;
967 else if (trimmedId == QByteArrayLiteral("INVERSE_PROJECTION_MATRIX"))
968 md.flags |= QSSGCustomShaderMetaData::UsesInverseProjectionMatrix;
969 else if (trimmedId == QByteArrayLiteral("VIEW_MATRIX"))
970 md.flags |= QSSGCustomShaderMetaData::UsesViewMatrix;
971 else if (trimmedId == QByteArrayLiteral("VAR_COLOR"))
972 md.flags |= QSSGCustomShaderMetaData::UsesVarColor;
973 else if (trimmedId == QByteArrayLiteral("SHARED_VARS"))
974 md.flags |= QSSGCustomShaderMetaData::UsesSharedVars;
975 else if (trimmedId == QByteArrayLiteral("IBL_ORIENTATION"))
976 md.flags |= QSSGCustomShaderMetaData::UsesIblOrientation;
977 else if (trimmedId == QByteArrayLiteral("LIGHTMAP"))
978 md.flags |= QSSGCustomShaderMetaData::UsesLightmap;
979 else if (trimmedId == QByteArrayLiteral("VIEW_INDEX"))
980 md.flags |= QSSGCustomShaderMetaData::UsesViewIndex;
981 else if (trimmedId == QByteArrayLiteral("INPUT"))
982 md.flags |= QSSGCustomShaderMetaData::UsesInputTexture;
983 else if (trimmedId == QByteArrayLiteral("CLEARCOAT_AMOUNT"))
984 md.flags |= QSSGCustomShaderMetaData::UsesClearcoat;
985 else if (trimmedId == QByteArrayLiteral("CLEARCOAT_FRESNEL_SCALE") ||
986 trimmedId == QByteArrayLiteral("CLEARCOAT_FRESNEL_BIAS"))
987 md.flags |= QSSGCustomShaderMetaData::UsesClearcoatFresnelScaleBias;
988 else if (trimmedId == QByteArrayLiteral("FRESNEL_SCALE") ||
989 trimmedId == QByteArrayLiteral("FRESNEL_BIAS"))
990 md.flags |= QSSGCustomShaderMetaData::UsesFresnelScaleBias;
991 else if (trimmedId == QByteArrayLiteral("MOTION_VECTOR_TEXTURE"))
992 md.flags |= QSSGCustomShaderMetaData::UsesMotionVectorTexture;
993 else if (trimmedId == QByteArrayLiteral("TRANSMISSION_FACTOR")) {
994 md.flags |= QSSGCustomShaderMetaData::UsesTransmission;
995 md.flags |= QSSGCustomShaderMetaData::UsesScreenTexture;
996 md.flags |= QSSGCustomShaderMetaData::UsesScreenMipTexture;
997 }
998
999 for (const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1000 if (trimmedId == subst.builtin) {
1001 QByteArray newExpr;
1002 newExpr.assign(subst.actualName);
1003 if (subst.multiViewDependent && multiViewCompatible) {
1004 if (subst.builtin.endsWith(QByteArrayLiteral("_TEXTURE"))
1005 || subst.builtin == QByteArrayLiteral("INPUT"))
1006 {
1007 newExpr += QByteArrayLiteral("Array"); // e.g. qt_depthTexture -> qt_depthTextureArray
1008 } else {
1009 newExpr += QByteArrayLiteral("[qt_viewIndex]"); // e.g. qt_viewProjectionMatrix -> qt_viewProjectionMatrix[qt_viewIndex]
1010 }
1011 }
1012 id.replace(subst.builtin, newExpr); // replace, not assignment, to keep whitespace etc.
1013 if (trimmedId == QByteArrayLiteral("BONE_TRANSFORMS")) {
1014 useJointTexState = 0;
1015 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1016 } else if (trimmedId == QByteArrayLiteral("BONE_NORMAL_TRANSFORMS")) {
1017 useJointNormalTexState = 0;
1018 md.flags |= QSSGCustomShaderMetaData::UsesSkinning;
1019 }
1020 if (trimmedId == QByteArrayLiteral("MORPH_POSITION") ||
1021 trimmedId == QByteArrayLiteral("MORPH_NORMAL") ||
1022 trimmedId == QByteArrayLiteral("MORPH_TANGENT") ||
1023 trimmedId == QByteArrayLiteral("MORPH_BINORMAL"))
1024 md.flags |= QSSGCustomShaderMetaData::UsesMorphing;
1025 break;
1026 }
1027 }
1028 result += id;
1029 }
1030 }
1031 break;
1032 case Tokenizer::Token_OpenParen:
1033 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1034 if (funcFinderState == 2) {
1035 result += QByteArrayLiteral("/*%QT_ARGS_");
1036 result += currentShadedFunc;
1037 result += QByteArrayLiteral("%*/");
1038 for (const QSSGCustomMaterialVariableSubstitution &subst : qssg_var_subst_tab) {
1039 if (currentShadedFunc == subst.builtin) {
1040 currentShadedFunc = subst.actualName.toByteArray();
1041 break;
1042 }
1043 }
1044 md.customFunctions.insert(currentShadedFunc);
1045 currentShadedFunc.clear();
1046 }
1047 funcFinderState = 0;
1048 break;
1049 case Tokenizer::Token_OpenBraket:
1050 // copy everything as-is up to the [
1051 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1052 if (useJointTexState == 0) {
1053 result += QByteArrayLiteral("(2 * (");
1054 ++useJointTexState;
1055 break;
1056 } else if (useJointNormalTexState == 0) {
1057 result += QByteArrayLiteral("(1 + 2 * (");
1058 ++useJointNormalTexState;
1059 break;
1060 }
1061
1062 if (useJointTexState >= 0)
1063 ++useJointTexState;
1064 else if (useJointNormalTexState >= 0)
1065 ++useJointNormalTexState;
1066 result += QByteArrayLiteral("[");
1067 break;
1068 case Tokenizer::Token_CloseBraket:
1069 // copy everything as-is up to the ]
1070 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos - 1);
1071 // This implementation will not allow mixed usages of BONE_TRANSFORMS and
1072 // BONE_NORMAL_TRANSFORMS.
1073 // For example, BONE_TRANSFORM[int(BONE_NORMAL_TRANFORMS[i][0].x)]
1074 // cannot be compiled successfully.
1075 if (useJointTexState <= 0 && useJointNormalTexState <= 0) {
1076 result += QByteArrayLiteral("]");
1077 break;
1078 }
1079 if (useJointTexState > 1) {
1080 result += QByteArrayLiteral("]");
1081 --useJointTexState;
1082 break;
1083 } else if (useJointNormalTexState > 1) {
1084 result += QByteArrayLiteral("]");
1085 --useJointNormalTexState;
1086 break;
1087 }
1088 result += QByteArrayLiteral("))");
1089 useJointTexState = -1;
1090 useJointNormalTexState = -1;
1091 break;
1092 default:
1093 result += QByteArray::fromRawData(lastPos, tok.pos - lastPos);
1094 break;
1095 }
1096 lastPos = tok.pos;
1097 t = tok.next();
1098 }
1099
1100 result += '\n';
1101
1102 workData->inputs = inputs;
1103 workData->outputs = outputs;
1104
1105 *codeAndMetaData = { result, md };
1106}
1107
1108void QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(
1109 QByteArray *dst,
1110 const CustomShaderPrepWorkData &workData,
1111 const ShaderCodeAndMetaData &codeAndMetaData,
1112 QSSGShaderCache::ShaderType type,
1113 bool multiViewCompatible,
1114 const StringPairList &baseUniforms,
1115 const StringPairList &baseInputs,
1116 const StringPairList &baseOutputs,
1117 const StringPairList &multiViewDependentSamplers,
1118 const StringPairList &multiViewDependentUniforms)
1119{
1120 StringPairList allUniforms = baseUniforms;
1121
1122 for (const StringPair &samplerTypeAndName : multiViewDependentSamplers) {
1123 if (multiViewCompatible)
1124 allUniforms.append({ "sampler2DArray", samplerTypeAndName.second });
1125 else
1126 allUniforms.append(samplerTypeAndName);
1127 }
1128
1129 const QSSGCustomShaderMetaData &md(codeAndMetaData.second);
1130
1131 // We either have qt_depthTexture or qt_depthTextureArray (or none of them),
1132 // but never both. We do not generally support binding a 2D texture to a
1133 // sampler2DArray binding point and vice versa. Therefore it is up to the
1134 // shader snippet to ifdef with QSHADER_VIEW_COUNT if it wants to support
1135 // both multiview and non-multiview rendering.
1136 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesDepthTexture)) {
1137 if (multiViewCompatible)
1138 allUniforms.append({ "sampler2DArray", "qt_depthTextureArray" });
1139 else
1140 allUniforms.append({ "sampler2D", "qt_depthTexture" });
1141 }
1142
1143 // And the same pattern for qt_screenTexture(Array).
1144 if ((md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenTexture) || md.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenMipTexture))) {
1145 if (multiViewCompatible)
1146 allUniforms.append({ "sampler2DArray", "qt_screenTextureArray" });
1147 else
1148 allUniforms.append({ "sampler2D", "qt_screenTexture" });
1149 }
1150
1151 // And for SSAO.
1152 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesAoTexture)) {
1153 if (multiViewCompatible)
1154 allUniforms.append({ "sampler2DArray", "qt_aoTextureArray" });
1155 else
1156 allUniforms.append({ "sampler2D", "qt_aoTexture" });
1157 }
1158
1159 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesNormalTexture))
1160 allUniforms.append({ "sampler2D", "qt_normalTexture" });
1161
1162 // Input texture for post-processing effects.
1163 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesInputTexture)) {
1164 if (multiViewCompatible)
1165 allUniforms.append({ "sampler2DArray", "qt_inputTextureArray" });
1166 else
1167 allUniforms.append({ "sampler2D", "qt_inputTexture" });
1168 }
1169
1170 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesLightmap))
1171 allUniforms.append({ "sampler2D", "qt_lightmap" });
1172
1173 if (md.flags.testFlag(QSSGCustomShaderMetaData::UsesMotionVectorTexture))
1174 allUniforms.append({ "sampler2D", "qt_motionVectorTexture" });
1175
1176 static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"uniforms\": [\n";
1177 static const char *metaEnd = " ]\n}*/\n#endif\n";
1178 dst->append(metaStart);
1179
1180 for (int i = 0, count = allUniforms.size(); i < count; ++i) {
1181 const auto &typeAndName(allUniforms[i]);
1182 dst->append(" { \"type\": \"" + typeAndName.first + "\", \"name\": \"" + typeAndName.second + "\" }");
1183 if (i < count - 1 || !multiViewDependentUniforms.isEmpty())
1184 dst->append(",");
1185 dst->append("\n");
1186 }
1187
1188 // multiViewDependentUniforms are not in allUniforms. For these we leave it to the
1189 // metadata processor to add an array suffix (e.g., qt_projectionMatrix[2]) when needed.
1190 for (int i = 0, count = multiViewDependentUniforms.size(); i < count; ++i) {
1191 const auto &typeAndName(multiViewDependentUniforms[i]);
1192 dst->append(" { \"type\": \"" + typeAndName.first + "\", \"name\": \"" + typeAndName.second + "\", \"multiview_dependent\": true }");
1193 if (i < count - 1)
1194 dst->append(",");
1195 dst->append("\n");
1196 }
1197
1198 dst->append(metaEnd);
1199
1200 const char *stageStr = type == QSSGShaderCache::ShaderType::Vertex ? "vertex" : "fragment";
1201 StringPairList allInputs = baseInputs;
1202 QVarLengthArray<bool, 16> inputIsFlat(allInputs.count(), false);
1203 for (const QByteArray &inputTypeAndName : workData.inputs) {
1204 const QByteArrayList typeAndName = inputTypeAndName.split(' ');
1205 if (typeAndName.size() == 2) {
1206 allInputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1207 inputIsFlat.append(false);
1208 } else if (typeAndName.size() == 3 && typeAndName[0].startsWith("flat")) {
1209 allInputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1210 inputIsFlat.append(true);
1211 }
1212 }
1213 if (!allInputs.isEmpty()) {
1214 static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"inputs\": [\n";
1215 static const char *metaEnd = " ]\n}*/\n#endif\n";
1216 dst->append(metaStart);
1217 for (int i = 0, count = allInputs.size(); i < count; ++i) {
1218 dst->append(" { \"type\": \"" + allInputs[i].first
1219 + "\", \"name\": \"" + allInputs[i].second
1220 + "\", \"stage\": \"" + stageStr
1221 + (inputIsFlat[i] ? "\", \"flat\": true" : "\"")
1222 + " }");
1223 if (i < count - 1)
1224 dst->append(",");
1225 dst->append("\n");
1226 }
1227 dst->append(metaEnd);
1228 }
1229
1230 StringPairList allOutputs = baseOutputs;
1231 QVarLengthArray<bool, 16> outputIsFlat(allOutputs.count(), false);
1232 for (const QByteArray &outputTypeAndName : workData.outputs) {
1233 const QByteArrayList typeAndName = outputTypeAndName.split(' ');
1234 if (typeAndName.size() == 2) {
1235 allOutputs.append({ typeAndName[0].trimmed(), typeAndName[1].trimmed() });
1236 outputIsFlat.append(false);
1237 } else if (typeAndName.size() == 3 && typeAndName[0].startsWith("flat")) {
1238 allOutputs.append({ typeAndName[1].trimmed(), typeAndName[2].trimmed() });
1239 outputIsFlat.append(true);
1240 }
1241 }
1242 if (!allOutputs.isEmpty()) {
1243 static const char *metaStart = "#ifdef QQ3D_SHADER_META\n/*{\n \"outputs\": [\n";
1244 static const char *metaEnd = " ]\n}*/\n#endif\n";
1245 dst->append(metaStart);
1246 for (int i = 0, count = allOutputs.size(); i < count; ++i) {
1247 dst->append(" { \"type\": \"" + allOutputs[i].first
1248 + "\", \"name\": \"" + allOutputs[i].second
1249 + "\", \"stage\": \"" + stageStr
1250 + (outputIsFlat[i] ? "\", \"flat\": true" : "\"")
1251 + " }");
1252 if (i < count - 1)
1253 dst->append(",");
1254 dst->append("\n");
1255 }
1256 dst->append(metaEnd);
1257 }
1258}
1259
1260QList<QByteArrayView> QtQuick3DEditorHelpers::CustomMaterial::preprocessorVars()
1261{
1262 QList<QByteArrayView> k;
1263 k.reserve(std::size(qssg_var_subst_tab));
1264 for (const auto &v : qssg_var_subst_tab)
1265 k.push_back(v.builtin);
1266 return k;
1267}
1268
1269QT_END_NAMESPACE
Combined button and popup list for selecting options.