private static bool GenerateShaderPassLit(StackLitMasterNode masterNode, Pass pass, GenerationMode mode, ShaderGenerator result, List <string> sourceAssetDependencyPaths)
        {
            if (mode == GenerationMode.ForReals || pass.UseInPreview)
            {
                pass.OnGeneratePass(masterNode);

                // apply master node options to active fields
                var activeFields = GetActiveFieldsFromMasterNode(masterNode, pass);

                // use standard shader pass generation
                bool vertexActive = masterNode.IsSlotConnected(StackLitMasterNode.PositionSlotId);
                return(HDSubShaderUtilities.GenerateShaderPass(masterNode, pass, mode, activeFields, result, sourceAssetDependencyPaths, vertexActive));
            }
            else
            {
                return(false);
            }
        }
 private static void AddPixelShaderSlotsForWriteNormalBufferPasses(StackLitMasterNode masterNode, ref Pass pass)
 {
     // See StackLit.hlsl:ConvertSurfaceDataToNormalData()
     // Note: We remove the slots we're adding as the editor will constantly regenerate the shader but
     // without recreating the nodes thus the passes in the subshader object.
     if (masterNode.coat.isOn)
     {
         // Check ConvertSurfaceDataToNormalData, in this case, we only need those:
         pass.PixelShaderSlots.Remove(StackLitMasterNode.CoatSmoothnessSlotId);
         pass.PixelShaderSlots.Add(StackLitMasterNode.CoatSmoothnessSlotId);
         pass.PixelShaderSlots.Remove(StackLitMasterNode.CoatNormalSlotId);
         pass.PixelShaderSlots.Add(StackLitMasterNode.CoatNormalSlotId);
     }
     else
     {
         pass.PixelShaderSlots.Remove(StackLitMasterNode.NormalSlotId);
         pass.PixelShaderSlots.Add(StackLitMasterNode.NormalSlotId);
         pass.PixelShaderSlots.Remove(StackLitMasterNode.LobeMixSlotId);
         pass.PixelShaderSlots.Add(StackLitMasterNode.LobeMixSlotId);
         pass.PixelShaderSlots.Remove(StackLitMasterNode.SmoothnessASlotId);
         pass.PixelShaderSlots.Add(StackLitMasterNode.SmoothnessASlotId);
         pass.PixelShaderSlots.Remove(StackLitMasterNode.SmoothnessBSlotId);
         pass.PixelShaderSlots.Add(StackLitMasterNode.SmoothnessBSlotId);
     }
     // Also, when geometricSpecularAA.isOn, might want to add SpecularAAScreenSpaceVarianceSlotId and SpecularAAThresholdSlotId,
     // since they affect smoothnesses in surfaceData directly. This is an implicit behavior for Lit, via the GBuffer pass.
     // Versus performance, might not be important for what it is used for, but SSR uses the roughness too so for now,
     // add them.
     if (masterNode.geometricSpecularAA.isOn) // TODOTODO || Normal Map Filtering is on
     {
         pass.PixelShaderSlots.Remove(StackLitMasterNode.SpecularAAScreenSpaceVarianceSlotId);
         pass.PixelShaderSlots.Add(StackLitMasterNode.SpecularAAScreenSpaceVarianceSlotId);
         pass.PixelShaderSlots.Remove(StackLitMasterNode.SpecularAAThresholdSlotId);
         pass.PixelShaderSlots.Add(StackLitMasterNode.SpecularAAThresholdSlotId);
     }
 }
        //
        // Reference for GetActiveFieldsFromMasterNode
        // -------------------------------------------
        //
        // Properties (enables etc):
        //
        //  ok+MFD -> material feature define: means we need a predicate, because we will transform it into a #define that match the material feature, shader_feature-defined, that the rest of the shader code uses.
        //
        //  ok+MFD masterNode.baseParametrization    --> even though we can just always transfer present fields (check with $SurfaceDescription.*) like specularcolor and metallic,
        //                                               we need to translate this into the _MATERIAL_FEATURE_SPECULAR_COLOR define.
        //
        //  ok masterNode.energyConservingSpecular
        //
        //  ~~~~ ok+MFD: these are almost all material features:
        //  masterNode.anisotropy
        //  masterNode.coat
        //  masterNode.coatNormal
        //  masterNode.dualSpecularLobe
        //  masterNode.dualSpecularLobeParametrization
        //  masterNode.capHazinessWrtMetallic           -> not a material feature define, as such, we will create a combined predicate for the HazyGlossMaxDielectricF0 slot dependency
        //                                                 instead of adding a #define in the template...
        //  masterNode.iridescence
        //  masterNode.subsurfaceScattering
        //  masterNode.transmission
        //
        //  ~~~~ ...ok+MFD: these are all material features
        //
        //  ok masterNode.receiveDecals
        //  ok masterNode.receiveSSR
        //  ok masterNode.geometricSpecularAA    --> check, a way to combine predicates and/or exclude passes: TODOTODO What about WRITE_NORMAL_BUFFER passes ? (ie smoothness)
        //  ok masterNode.specularOcclusion      --> no use for it though! see comments.
        //
        //  ~~~~ ok+D: these require translation to defines also...
        //
        //  masterNode.anisotropyForAreaLights
        //  masterNode.recomputeStackPerLight
        //  masterNode.shadeBaseUsingRefractedAngles
        //  masterNode.debug

        // Inputs: Most inputs don't need a specific predicate in addition to the "present field predicate", ie the $SurfaceDescription.*,
        //         but in some special cases we check connectivity to avoid processing the default value for nothing...
        //         (see specular occlusion with _MASKMAP and _BENTNORMALMAP in LitData, or _TANGENTMAP, _BENTNORMALMAP, etc. which act a bit like that
        //         although they also avoid sampling in that case, but default tiny texture map sampling isn't a big hit since they are all cached once
        //         a default "unityTexWhite" is sampled, it is cached for everyone defaulting to white...)
        //
        // ok+ means there's a specific additional predicate
        //
        // ok masterNode.BaseColorSlotId
        // ok masterNode.NormalSlotId
        //
        // ok+ masterNode.BentNormalSlotId     --> Dependency of the predicate on IsSlotConnected avoids processing even if the slots
        // ok+ masterNode.TangentSlotId            are always there so any pass that declares its use in PixelShaderSlots will have the field in SurfaceDescription,
        //                                         but it's not necessarily useful (if slot isnt connected, waste processing on potentially static expressions if
        //                                         shader compiler cant optimize...and even then, useless to have static override value for those.)
        //
        //                                         TODOTODO: Note you could have the same argument for NormalSlot (which we dont exclude with a predicate).
        //                                         Also and anyways, the compiler is smart enough not to do the TS to WS matrix multiply on a (0,0,1) vector.
        //
        // ok+ masterNode.CoatNormalSlotId       -> we already have a "material feature" coat normal map so can use that instead, although using that former, we assume the coat normal slot
        //                                         will be there, but it's ok, we can #ifdef the code on the material feature define, and use the $SurfaceDescription.CoatNormal predicate
        //                                         for the actual assignment,
        //                                         although for that one we could again
        //                                         use the "connected" condition like for tangent and bentnormal
        //
        // The following are all ok, no need beyond present field predicate, ie $SurfaceDescription.*,
        // except special cases where noted
        //
        // ok masterNode.SubsurfaceMaskSlotId
        // ok masterNode.ThicknessSlotId
        // ok masterNode.DiffusionProfileHashSlotId
        // ok masterNode.IridescenceMaskSlotId
        // ok masterNode.IridescenceThicknessSlotId
        // ok masterNode.SpecularColorSlotId
        // ok masterNode.DielectricIorSlotId
        // ok masterNode.MetallicSlotId
        // ok masterNode.EmissionSlotId
        // ok masterNode.SmoothnessASlotId
        // ok masterNode.SmoothnessBSlotId
        // ok+ masterNode.AmbientOcclusionSlotId    -> defined a specific predicate, but not used, see StackLitData.
        // ok masterNode.AlphaSlotId
        // ok masterNode.AlphaClipThresholdSlotId
        // ok masterNode.AnisotropyASlotId
        // ok masterNode.AnisotropyBSlotId
        // ok masterNode.SpecularAAScreenSpaceVarianceSlotId
        // ok masterNode.SpecularAAThresholdSlotId
        // ok masterNode.CoatSmoothnessSlotId
        // ok masterNode.CoatIorSlotId
        // ok masterNode.CoatThicknessSlotId
        // ok masterNode.CoatExtinctionSlotId
        // ok masterNode.LobeMixSlotId
        // ok masterNode.HazinessSlotId
        // ok masterNode.HazeExtentSlotId
        // ok masterNode.HazyGlossMaxDielectricF0SlotId     -> No need for a predicate, the needed predicate is the combined (capHazinessWrtMetallic + HazyGlossMaxDielectricF0)
        //                                                     "leaking case": if the 2 are true, but we're not in metallic mode, the capHazinessWrtMetallic property is wrong,
        //                                                     that means the master node is really misconfigured, spew an error, should never happen...
        //                                                     If it happens, it's because we forgot UpdateNodeAfterDeserialization() call when modifying the capHazinessWrtMetallic or baseParametrization
        //                                                     properties, maybe through debug etc.
        //
        // ok masterNode.DistortionSlotId            -> Warning: peculiarly, instead of using $SurfaceDescription.Distortion and DistortionBlur,
        // ok masterNode.DistortionBlurSlotId           we do an #if (SHADERPASS == SHADERPASS_DISTORTION) in the template, instead of
        //                                              relying on other passed NOT to include the DistortionSlotId in their PixelShaderSlots!!

        // Other to deal with, and
        // Common between Lit and StackLit:
        //
        // doubleSidedMode, alphaTest, receiveDecals,
        // surfaceType, alphaMode, blendPreserveSpecular, transparencyFog,
        // distortion, distortionMode, distortionDepthTest,
        // sortPriority (int)
        // geometricSpecularAA, energyConservingSpecular, specularOcclusion
        //

        private static ActiveFields GetActiveFieldsFromMasterNode(AbstractMaterialNode iMasterNode, Pass pass)
        {
            var activeFields     = new ActiveFields();
            var baseActiveFields = activeFields.baseInstance;

            StackLitMasterNode masterNode = iMasterNode as StackLitMasterNode;

            if (masterNode == null)
            {
                return(activeFields);
            }

            if (masterNode.doubleSidedMode != DoubleSidedMode.Disabled)
            {
                if (pass.ShaderPassName != "SHADERPASS_MOTION_VECTORS") // HACK to get around lack of a good interpolator dependency system
                {                                                       // we need to be able to build interpolators using multiple input structs
                                                                        // also: should only require isFrontFace if Normals are required...
                    // Important: the following is used in SharedCode.template.hlsl for determining the normal flip mode
                    baseActiveFields.Add("FragInputs.isFrontFace");
                }
            }

            if (masterNode.alphaTest.isOn)
            {
                if (pass.PixelShaderUsesSlot(StackLitMasterNode.AlphaClipThresholdSlotId))
                {
                    baseActiveFields.Add("AlphaTest");
                }
            }

            if (masterNode.surfaceType != SurfaceType.Opaque)
            {
                if (masterNode.transparencyFog.isOn)
                {
                    baseActiveFields.Add("AlphaFog");
                }

                if (masterNode.blendPreserveSpecular.isOn)
                {
                    baseActiveFields.Add("BlendMode.PreserveSpecular");
                }
            }

            //
            // Predicates to change into defines:
            //

            // Even though we can just always transfer the present (check with $SurfaceDescription.*) fields like specularcolor
            // and metallic, we still need to know the baseParametrization in the template to translate into the
            // _MATERIAL_FEATURE_SPECULAR_COLOR define:
            if (masterNode.baseParametrization == StackLit.BaseParametrization.SpecularColor)
            {
                baseActiveFields.Add("BaseParametrization.SpecularColor");
            }
            if (masterNode.energyConservingSpecular.isOn) // No defines, suboption of BaseParametrization.SpecularColor
            {
                baseActiveFields.Add("EnergyConservingSpecular");
            }
            if (masterNode.anisotropy.isOn)
            {
                baseActiveFields.Add("Material.Anisotropy");
            }
            if (masterNode.coat.isOn)
            {
                baseActiveFields.Add("Material.Coat");
                if (pass.PixelShaderUsesSlot(StackLitMasterNode.CoatMaskSlotId))
                {
                    var  coatMaskSlot = masterNode.FindSlot <Vector1MaterialSlot>(StackLitMasterNode.CoatMaskSlotId);
                    bool connected    = masterNode.IsSlotConnected(StackLitMasterNode.CoatMaskSlotId);

                    if (connected || (coatMaskSlot.value != 0.0f && coatMaskSlot.value != 1.0f))
                    {
                        baseActiveFields.Add("CoatMask");
                    }
                    else if (coatMaskSlot.value == 0.0f)
                    {
                        baseActiveFields.Add("CoatMaskZero");
                    }
                    else if (coatMaskSlot.value == 1.0f)
                    {
                        baseActiveFields.Add("CoatMaskOne");
                    }
                }
            }
            if (masterNode.coatNormal.isOn)
            {
                baseActiveFields.Add("Material.CoatNormal");
            }
            if (masterNode.dualSpecularLobe.isOn)
            {
                baseActiveFields.Add("Material.DualSpecularLobe");
                if (masterNode.dualSpecularLobeParametrization == StackLit.DualSpecularLobeParametrization.HazyGloss)
                {
                    baseActiveFields.Add("DualSpecularLobeParametrization.HazyGloss");
                    // Option for baseParametrization == Metallic && DualSpecularLobeParametrization == HazyGloss:
                    if (masterNode.capHazinessWrtMetallic.isOn && pass.PixelShaderUsesSlot(StackLitMasterNode.HazyGlossMaxDielectricF0SlotId))
                    {
                        // check the supporting slot is there (although masternode should deal with having a consistent property config)
                        var maxDielectricF0Slot = masterNode.FindSlot <Vector1MaterialSlot>(StackLitMasterNode.HazyGlossMaxDielectricF0SlotId);

                        if (maxDielectricF0Slot != null)
                        {
                            // Again we assume masternode has HazyGlossMaxDielectricF0 which should always be the case
                            // if capHazinessWrtMetallic.isOn.
                            baseActiveFields.Add("CapHazinessIfNotMetallic");
                        }
                    }
                }
            }
            if (masterNode.iridescence.isOn)
            {
                baseActiveFields.Add("Material.Iridescence");
            }
            if (masterNode.subsurfaceScattering.isOn && masterNode.surfaceType != SurfaceType.Transparent)
            {
                baseActiveFields.Add("Material.SubsurfaceScattering");
            }
            if (masterNode.transmission.isOn)
            {
                baseActiveFields.Add("Material.Transmission");
            }

            // Advanced:
            if (masterNode.anisotropyForAreaLights.isOn)
            {
                baseActiveFields.Add("AnisotropyForAreaLights");
            }
            if (masterNode.recomputeStackPerLight.isOn)
            {
                baseActiveFields.Add("RecomputeStackPerLight");
            }
            if (masterNode.honorPerLightMinRoughness.isOn)
            {
                baseActiveFields.Add("HonorPerLightMinRoughness");
            }
            if (masterNode.shadeBaseUsingRefractedAngles.isOn)
            {
                baseActiveFields.Add("ShadeBaseUsingRefractedAngles");
            }
            if (masterNode.debug.isOn)
            {
                baseActiveFields.Add("StackLitDebug");
            }

            //
            // Other property predicates:
            //

            if (!masterNode.receiveDecals.isOn)
            {
                baseActiveFields.Add("DisableDecals");
            }

            if (!masterNode.receiveSSR.isOn)
            {
                baseActiveFields.Add("DisableSSR");
            }

            if (masterNode.addPrecomputedVelocity.isOn)
            {
                baseActiveFields.Add("AddPrecomputedVelocity");
            }

            // Note here we combine an "enable"-like predicate and the $SurfaceDescription.(slotname) predicate
            // into a single $GeometricSpecularAA pedicate.
            //
            // ($SurfaceDescription.* predicates are useful to make sure the field is present in the struct in the template.
            // The field will be present if both the master node and pass have the slotid, see this set intersection we make
            // in GenerateSurfaceDescriptionStruct(), with HDSubShaderUtilities.FindMaterialSlotsOnNode().)
            //
            // Normally, since the feature enable adds the required slots, only the $SurfaceDescription.* would be required,
            // but some passes might not need it and not declare the PixelShaderSlot, or, inversely, the pass might not
            // declare it as a way to avoid it.
            //
            // IE this has also the side effect to disable geometricSpecularAA - even if "on" - for passes that don't explicitly
            // advertise these slots(eg for a general feature, with separate "enable" and "field present" predicates, the
            // template could take a default value and process it anyway if a feature is "on").
            //
            // (Note we can achieve the same results in the template on just single predicates by making defines out of them,
            // and using #if defined() && etc)
            bool haveSomeSpecularAA = false; // TODOTODO in prevision of normal texture filtering

            if (masterNode.geometricSpecularAA.isOn &&
                pass.PixelShaderUsesSlot(StackLitMasterNode.SpecularAAThresholdSlotId) &&
                pass.PixelShaderUsesSlot(StackLitMasterNode.SpecularAAScreenSpaceVarianceSlotId))
            {
                haveSomeSpecularAA = true;
                baseActiveFields.Add("GeometricSpecularAA");
            }
            if (haveSomeSpecularAA)
            {
                baseActiveFields.Add("SpecularAA");
            }

            if (masterNode.screenSpaceSpecularOcclusionBaseMode != StackLitMasterNode.SpecularOcclusionBaseMode.Off ||
                masterNode.dataBasedSpecularOcclusionBaseMode != StackLitMasterNode.SpecularOcclusionBaseMode.Off)
            {
                // activates main define
                baseActiveFields.Add("SpecularOcclusion");
            }

            baseActiveFields.Add("ScreenSpaceSpecularOcclusionBaseMode." + masterNode.screenSpaceSpecularOcclusionBaseMode.ToString());
            if (StackLitMasterNode.SpecularOcclusionModeUsesVisibilityCone(masterNode.screenSpaceSpecularOcclusionBaseMode))
            {
                baseActiveFields.Add("ScreenSpaceSpecularOcclusionAOConeSize." + masterNode.screenSpaceSpecularOcclusionAOConeSize.ToString());
                baseActiveFields.Add("ScreenSpaceSpecularOcclusionAOConeDir." + masterNode.screenSpaceSpecularOcclusionAOConeDir.ToString());
            }

            baseActiveFields.Add("DataBasedSpecularOcclusionBaseMode." + masterNode.dataBasedSpecularOcclusionBaseMode.ToString());
            if (StackLitMasterNode.SpecularOcclusionModeUsesVisibilityCone(masterNode.dataBasedSpecularOcclusionBaseMode))
            {
                baseActiveFields.Add("DataBasedSpecularOcclusionAOConeSize." + masterNode.dataBasedSpecularOcclusionAOConeSize.ToString());
            }

            // Set bent normal fixup predicate if needed:
            if (masterNode.SpecularOcclusionUsesBentNormal())
            {
                baseActiveFields.Add("SpecularOcclusionConeFixupMethod." + masterNode.specularOcclusionConeFixupMethod.ToString());
            }

            //
            // Input special-casing predicates:
            //

            if (masterNode.IsSlotConnected(StackLitMasterNode.BentNormalSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.BentNormalSlotId))
            {
                baseActiveFields.Add("BentNormal");
            }

            if (masterNode.IsSlotConnected(StackLitMasterNode.TangentSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.TangentSlotId))
            {
                baseActiveFields.Add("Tangent");
            }

            // The following idiom enables an optimization on feature ports that don't have an enable switch in the settings
            // view, where the default value might not produce a visual result and incur a processing cost we want to avoid.
            // For ambient occlusion, this is the case for the SpecularOcclusion calculations which also depend on it,
            // where a value of 1 will produce no results.
            // See SpecularOcclusion, we don't optimize out this case...
            if (pass.PixelShaderUsesSlot(StackLitMasterNode.AmbientOcclusionSlotId))
            {
                bool connected            = masterNode.IsSlotConnected(StackLitMasterNode.AmbientOcclusionSlotId);
                var  ambientOcclusionSlot = masterNode.FindSlot <Vector1MaterialSlot>(StackLitMasterNode.AmbientOcclusionSlotId);
                // master node always has it, assert ambientOcclusionSlot != null
                if (connected || ambientOcclusionSlot.value != ambientOcclusionSlot.defaultValue)
                {
                    baseActiveFields.Add("AmbientOcclusion");
                }
            }

            if (masterNode.IsSlotConnected(StackLitMasterNode.CoatNormalSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.CoatNormalSlotId))
            {
                baseActiveFields.Add("CoatNormal");
            }

            if (masterNode.IsSlotConnected(StackLitMasterNode.LightingSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.LightingSlotId))
            {
                baseActiveFields.Add("LightingGI");
            }
            if (masterNode.IsSlotConnected(StackLitMasterNode.BackLightingSlotId) && pass.PixelShaderUsesSlot(StackLitMasterNode.BackLightingSlotId))
            {
                baseActiveFields.Add("BackLightingGI");
            }

            if (masterNode.depthOffset.isOn && pass.PixelShaderUsesSlot(StackLitMasterNode.DepthOffsetSlotId))
            {
                baseActiveFields.Add("DepthOffset");
            }

            return(activeFields);
        }
Beispiel #4
0
        public StackLitSettingsView(StackLitMasterNode node) : base(node)
        {
            m_Node = node;
            PropertySheet ps = new PropertySheet();

            int indentLevel = 0;

            ps.Add(new PropertyRow(CreateLabel("Surface Type", indentLevel)), (row) =>
            {
                row.Add(new EnumField(SurfaceType.Opaque), (field) =>
                {
                    field.value = m_Node.surfaceType;
                    field.RegisterValueChangedCallback(ChangeSurfaceType);
                });
            });

            if (m_Node.surfaceType == SurfaceType.Transparent)
            {
                ++indentLevel;

                // No refraction in StackLit, always show this:
                ps.Add(new PropertyRow(CreateLabel("Blending Mode", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLitMasterNode.AlphaModeLit.Additive), (field) =>
                    {
                        field.value = GetAlphaModeLit(m_Node.alphaMode);
                        field.RegisterValueChangedCallback(ChangeBlendMode);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Blend Preserves Specular", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.blendPreserveSpecular.isOn;
                        toggle.OnToggleChanged(ChangeBlendPreserveSpecular);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Fog", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.transparencyFog.isOn;
                        toggle.OnToggleChanged(ChangeTransparencyFog);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Distortion", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.distortion.isOn;
                        toggle.OnToggleChanged(ChangeDistortion);
                    });
                });

                if (m_Node.distortion.isOn)
                {
                    ++indentLevel;
                    ps.Add(new PropertyRow(CreateLabel("Distortion Blend Mode", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(DistortionMode.Add), (field) =>
                        {
                            field.value = m_Node.distortionMode;
                            field.RegisterValueChangedCallback(ChangeDistortionMode);
                        });
                    });
                    ps.Add(new PropertyRow(CreateLabel("Distortion Depth Test", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.distortionDepthTest.isOn;
                            toggle.OnToggleChanged(ChangeDistortionDepthTest);
                        });
                    });
                    --indentLevel;
                }

                m_SortPiorityField = new IntegerField();
                ps.Add(new PropertyRow(CreateLabel("Sort Priority", indentLevel)), (row) =>
                {
                    row.Add(m_SortPiorityField, (field) =>
                    {
                        field.value = m_Node.sortPriority;
                        field.RegisterValueChangedCallback(ChangeSortPriority);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Depth Write", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.zWrite.isOn;
                        toggle.OnToggleChanged(ChangeZWrite);
                    });
                });

                if (m_Node.doubleSidedMode == DoubleSidedMode.Disabled)
                {
                    ps.Add(new PropertyRow(CreateLabel("Cull Mode", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(m_Node.transparentCullMode), (e) =>
                        {
                            e.value = m_Node.transparentCullMode;
                            e.RegisterValueChangedCallback(ChangeTransparentCullMode);
                        });
                    });
                }

                ps.Add(new PropertyRow(CreateLabel("Depth Test", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(m_Node.zTest), (e) =>
                    {
                        e.value = m_Node.zTest;
                        e.RegisterValueChangedCallback(ChangeZTest);
                    });
                });

                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Alpha Clipping", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.alphaTest.isOn;
                    toggle.OnToggleChanged(ChangeAlphaTest);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Double-Sided", indentLevel)), (row) =>
            {
                row.Add(new EnumField(DoubleSidedMode.Disabled), (field) =>
                {
                    field.value = m_Node.doubleSidedMode;
                    field.RegisterValueChangedCallback(ChangeDoubleSidedMode);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Fragment Normal Space", indentLevel)), (row) =>
            {
                row.Add(new EnumField(NormalDropOffSpace.Tangent), (field) =>
                {
                    field.value = m_Node.normalDropOffSpace;
                    field.RegisterValueChangedCallback(ChangeSpaceOfNormalDropOffMode);
                });
            });

            // Rest of UI looks like this:
            //
            //  baseParametrization
            //    energyConservingSpecular
            //
            //  anisotropy
            //  coat
            //  coatNormal
            //  dualSpecularLobe
            //    dualSpecularLobeParametrization
            //    capHazinessWrtMetallic
            //  iridescence
            //  subsurfaceScattering
            //  transmission
            //
            //  receiveDecals
            //  receiveSSR
            //  addPrecomputedVelocity
            //  geometricSpecularAA
            //  specularOcclusion
            //
            //  anisotropyForAreaLights
            //  recomputeStackPerLight
            //  shadeBaseUsingRefractedAngles

            // Base parametrization:

            ps.Add(new PropertyRow(CreateLabel("Base Color Parametrization", indentLevel)), (row) =>
            {
                row.Add(new EnumField(StackLit.BaseParametrization.BaseMetallic), (field) =>
                {
                    field.value = m_Node.baseParametrization;
                    field.RegisterValueChangedCallback(ChangeBaseParametrization);
                });
            });

            if (m_Node.baseParametrization == StackLit.BaseParametrization.SpecularColor)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Energy Conserving Specular", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.energyConservingSpecular.isOn;
                        toggle.OnToggleChanged(ChangeEnergyConservingSpecular);
                    });
                });
                --indentLevel;
            }

            // Material type enables:
            ps.Add(new PropertyRow(CreateLabel("Material Core Features", indentLevel)), (row) => {});
            ++indentLevel;

            ps.Add(new PropertyRow(CreateLabel("Anisotropy", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.anisotropy.isOn;
                    toggle.OnToggleChanged(ChangeAnisotropy);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Coat", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.coat.isOn;
                    toggle.OnToggleChanged(ChangeCoat);
                });
            });

            if (m_Node.coat.isOn)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Coat Normal", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.coatNormal.isOn;
                        toggle.OnToggleChanged(ChangeCoatNormal);
                    });
                });
                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Dual Specular Lobe", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.dualSpecularLobe.isOn;
                    toggle.OnToggleChanged(ChangeDualSpecularLobe);
                });
            });

            if (m_Node.dualSpecularLobe.isOn)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Dual SpecularLobe Parametrization", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLit.DualSpecularLobeParametrization.HazyGloss), (field) =>
                    {
                        field.value = m_Node.dualSpecularLobeParametrization;
                        field.RegisterValueChangedCallback(ChangeDualSpecularLobeParametrization);
                    });
                });
                if ((m_Node.baseParametrization == StackLit.BaseParametrization.BaseMetallic) &&
                    (m_Node.dualSpecularLobeParametrization == StackLit.DualSpecularLobeParametrization.HazyGloss))
                {
                    ps.Add(new PropertyRow(CreateLabel("Cap Haziness For Non Metallic", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.capHazinessWrtMetallic.isOn;
                            toggle.OnToggleChanged(ChangeCapHazinessWrtMetallic);
                        });
                    });
                }
                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Iridescence", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.iridescence.isOn;
                    toggle.OnToggleChanged(ChangeIridescence);
                });
            });

            if (m_Node.surfaceType != SurfaceType.Transparent)
            {
                ps.Add(new PropertyRow(CreateLabel("Subsurface Scattering", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.subsurfaceScattering.isOn;
                        toggle.OnToggleChanged(ChangeSubsurfaceScattering);
                    });
                });
            }

            ps.Add(new PropertyRow(CreateLabel("Transmission", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.transmission.isOn;
                    toggle.OnToggleChanged(ChangeTransmission);
                });
            });
            --indentLevel; // ...Material type enables.

            ps.Add(new PropertyRow(CreateLabel("Receive Decals", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.receiveDecals.isOn;
                    toggle.OnToggleChanged(ChangeReceiveDecals);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Receive SSR", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.receiveSSR.isOn;
                    toggle.OnToggleChanged(ChangeReceiveSSR);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Add Precomputed Velocity", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.addPrecomputedVelocity.isOn;
                    toggle.OnToggleChanged(ChangeAddPrecomputedVelocity);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Geometric Specular AA", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.geometricSpecularAA.isOn;
                    toggle.OnToggleChanged(ChangeGeometricSpecularAA);
                });
            });

            //ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (main enable)", indentLevel)), (row) =>
            //{
            //    row.Add(new Toggle(), (toggle) =>
            //    {
            //        toggle.value = m_Node.specularOcclusion.isOn;
            //        toggle.OnToggleChanged(ChangeSpecularOcclusion);
            //    });
            //});

            // SpecularOcclusion from SSAO
            if (m_Node.devMode.isOn)
            {
                // Only in dev mode do we show controls for SO fed from SSAO: otherwise, we keep the default which is DirectFromAO
                ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (from SSAO)", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionBaseMode.DirectFromAO), (field) =>
                    {
                        field.value = m_Node.screenSpaceSpecularOcclusionBaseMode;
                        field.RegisterValueChangedCallback(ChangeScreenSpaceSpecularOcclusionBaseMode);
                    });
                });
                if (StackLitMasterNode.SpecularOcclusionModeUsesVisibilityCone(m_Node.screenSpaceSpecularOcclusionBaseMode))
                {
                    ++indentLevel;
                    ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (SS) AO Cone Weight", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionAOConeSize.CosWeightedAO), (field) =>
                        {
                            field.value = m_Node.screenSpaceSpecularOcclusionAOConeSize;
                            field.RegisterValueChangedCallback(ChangeScreenSpaceSpecularOcclusionAOConeSize);
                        });
                    });
                    ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (SS) AO Cone Dir", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionAOConeDir.ShadingNormal), (field) =>
                        {
                            field.value = m_Node.screenSpaceSpecularOcclusionAOConeDir;
                            field.RegisterValueChangedCallback(ChangeScreenSpaceSpecularOcclusionAOConeDir);
                        });
                    });
                    --indentLevel;
                }
            }

            // SpecularOcclusion from input AO (baked or data-based SO)
            {
                ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (from input AO)", indentLevel)), (row) =>
                {
                    if (m_Node.devMode.isOn)
                    {
                        row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionBaseMode.DirectFromAO), (field) =>
                        {
                            field.value = m_Node.dataBasedSpecularOcclusionBaseMode;
                            field.RegisterValueChangedCallback(ChangeDataBasedSpecularOcclusionBaseMode);
                        });
                    }
                    else
                    {
                        row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionBaseModeSimple.DirectFromAO), (field) =>
                        {
                            // In non-dev mode, parse any enum value set to a method not shown in the simple UI as SPTD (highest quality) method:
                            StackLitMasterNode.SpecularOcclusionBaseModeSimple simpleUIEnumValue =
                                Enum.TryParse(m_Node.dataBasedSpecularOcclusionBaseMode.ToString(), out StackLitMasterNode.SpecularOcclusionBaseModeSimple parsedValue) ?
                                parsedValue : StackLitMasterNode.SpecularOcclusionBaseModeSimple.SPTDIntegrationOfBentAO;
                            field.value = simpleUIEnumValue;
                            field.RegisterValueChangedCallback(ChangeDataBasedSpecularOcclusionBaseModeSimpleUI);
                        });
                    }
                });
                if (StackLitMasterNode.SpecularOcclusionModeUsesVisibilityCone(m_Node.dataBasedSpecularOcclusionBaseMode))
                {
                    ++indentLevel;
                    ps.Add(new PropertyRow(CreateLabel("Specular Occlusion AO Cone Weight", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionAOConeSize.CosWeightedBentCorrectAO), (field) =>
                        {
                            field.value = m_Node.dataBasedSpecularOcclusionAOConeSize;
                            field.RegisterValueChangedCallback(ChangeDataBasedSpecularOcclusionAOConeSize);
                        });
                    });
                    --indentLevel;
                }
            }

            if (m_Node.SpecularOcclusionUsesBentNormal())
            {
                if (m_Node.devMode.isOn)
                {
                    ps.Add(new PropertyRow(CreateLabel("Specular Occlusion Bent Cone Fixup", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionConeFixupMethod.Off), (field) =>
                        {
                            field.value = m_Node.specularOcclusionConeFixupMethod;
                            field.RegisterValueChangedCallback(ChangeSpecularOcclusionConeFixupMethod);
                        });
                    });
                }
                else
                {
                    // Just show a simple toggle when not in dev mode
                    ps.Add(new PropertyRow(CreateLabel("Specular Occlusion Bent Cone Fixup", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.specularOcclusionConeFixupMethod != StackLitMasterNode.SpecularOcclusionConeFixupMethod.Off;
                            toggle.OnToggleChanged(ChangeSpecularOcclusionConeFixupMethodSimpleUI);
                        });
                    });
                }
            }

            ps.Add(new PropertyRow(CreateLabel("Support LOD CrossFade", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.supportLodCrossFade.isOn;
                    toggle.OnToggleChanged(ChangeSupportLODCrossFade);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Advanced Options", indentLevel)), (row) => {});
            ++indentLevel;

            ps.Add(new PropertyRow(CreateLabel("Anisotropy For Area Lights", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.anisotropyForAreaLights.isOn;
                    toggle.OnToggleChanged(ChangeAnisotropyForAreaLights);
                });
            });

            // Per Punctual/Directional Lights
            {
                ps.Add(new PropertyRow(CreateLabel("Per Punctual/Directional Lights:", indentLevel)), (row) => { });
                ++indentLevel;

                if (m_Node.coat.isOn)
                {
                    ps.Add(new PropertyRow(CreateLabel("Base Layer Uses Refracted Angles", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.shadeBaseUsingRefractedAngles.isOn;
                            toggle.OnToggleChanged(ChangeShadeBaseUsingRefractedAngles);
                        });
                    });
                }
                if (m_Node.coat.isOn || m_Node.iridescence.isOn)
                {
                    ps.Add(new PropertyRow(CreateLabel("Recompute Stack & Iridescence", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.recomputeStackPerLight.isOn;
                            toggle.OnToggleChanged(ChangeRecomputeStackPerLight);
                        });
                    });
                }
                ps.Add(new PropertyRow(CreateLabel("Honor Per Light Max Smoothness", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.honorPerLightMinRoughness.isOn;
                        toggle.OnToggleChanged(ChangeHonorPerLightMinRoughness);
                    });
                });

                --indentLevel;
            } // Per Punctual/Directional Lights

            // Uncomment to show the dev mode UI:
            //
            //ps.Add(new PropertyRow(CreateLabel("Enable Dev Mode", indentLevel)), (row) =>
            //{
            //    row.Add(new Toggle(), (toggle) =>
            //    {
            //        toggle.value = m_Node.devMode.isOn;
            //        toggle.OnToggleChanged(ChangeDevMode);
            //    });
            //});

            if (m_Node.devMode.isOn)
            {
                ps.Add(new PropertyRow(CreateLabel("Show And Enable StackLit Debugs", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.debug.isOn;
                        toggle.OnToggleChanged(ChangeDebug);
                    });
                });
            }

            ps.Add(new PropertyRow(CreateLabel("Override Baked GI", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.overrideBakedGI.isOn;
                    toggle.OnToggleChanged(ChangeoverrideBakedGI);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Depth Offset", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.depthOffset.isOn;
                    toggle.OnToggleChanged(ChangeDepthOffset);
                });
            });

            --indentLevel; //...Advanced options

            Add(ps);
            Add(GetShaderGUIOverridePropertySheet());
        }
Beispiel #5
0
        public StackLitSettingsView(StackLitMasterNode node)
        {
            m_Node = node;
            PropertySheet ps = new PropertySheet();

            int indentLevel = 0;

            ps.Add(new PropertyRow(CreateLabel("Surface Type", indentLevel)), (row) =>
            {
                row.Add(new EnumField(SurfaceType.Opaque), (field) =>
                {
                    field.value = m_Node.surfaceType;
                    field.RegisterValueChangedCallback(ChangeSurfaceType);
                });
            });

            if (m_Node.surfaceType == SurfaceType.Transparent)
            {
                ++indentLevel;

                // No refraction in StackLit, always show this:
                ps.Add(new PropertyRow(CreateLabel("Blending Mode", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLitMasterNode.AlphaModeLit.Additive), (field) =>
                    {
                        field.value = GetAlphaModeLit(m_Node.alphaMode);
                        field.RegisterValueChangedCallback(ChangeBlendMode);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Blend Preserves Specular", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.blendPreserveSpecular.isOn;
                        toggle.OnToggleChanged(ChangeBlendPreserveSpecular);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Fog", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.transparencyFog.isOn;
                        toggle.OnToggleChanged(ChangeTransparencyFog);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Distortion", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.distortion.isOn;
                        toggle.OnToggleChanged(ChangeDistortion);
                    });
                });

                if (m_Node.distortion.isOn)
                {
                    ++indentLevel;
                    ps.Add(new PropertyRow(CreateLabel("Mode", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(DistortionMode.Add), (field) =>
                        {
                            field.value = m_Node.distortionMode;
                            field.RegisterValueChangedCallback(ChangeDistortionMode);
                        });
                    });
                    ps.Add(new PropertyRow(CreateLabel("Depth Test", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.distortionDepthTest.isOn;
                            toggle.OnToggleChanged(ChangeDistortionDepthTest);
                        });
                    });
                    --indentLevel;
                }

                m_SortPiorityField = new IntegerField();
                ps.Add(new PropertyRow(CreateLabel("Sort Priority", indentLevel)), (row) =>
                {
                    row.Add(m_SortPiorityField, (field) =>
                    {
                        field.value = m_Node.sortPriority;
                        field.RegisterValueChangedCallback(ChangeSortPriority);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("ZWrite", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.zWrite.isOn;
                        toggle.OnToggleChanged(ChangeZWrite);
                    });
                });

                if (m_Node.doubleSidedMode == DoubleSidedMode.Disabled)
                {
                    ps.Add(new PropertyRow(CreateLabel("Cull Mode", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(m_Node.transparentCullMode), (e) =>
                        {
                            e.value = m_Node.transparentCullMode;
                            e.RegisterValueChangedCallback(ChangeTransparentCullMode);
                        });
                    });
                }

                ps.Add(new PropertyRow(CreateLabel("Z Test", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(m_Node.zTest), (e) =>
                    {
                        e.value = m_Node.zTest;
                        e.RegisterValueChangedCallback(ChangeZTest);
                    });
                });

                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Alpha Clipping", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.alphaTest.isOn;
                    toggle.OnToggleChanged(ChangeAlphaTest);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Double-Sided", indentLevel)), (row) =>
            {
                row.Add(new EnumField(DoubleSidedMode.Disabled), (field) =>
                {
                    field.value = m_Node.doubleSidedMode;
                    field.RegisterValueChangedCallback(ChangeDoubleSidedMode);
                });
            });

            // Rest of UI looks like this:
            //
            //  baseParametrization
            //    energyConservingSpecular
            //
            //  anisotropy
            //  coat
            //  coatNormal
            //  dualSpecularLobe
            //    dualSpecularLobeParametrization
            //    capHazinessWrtMetallic
            //  iridescence
            //  subsurfaceScattering
            //  transmission
            //
            //  receiveDecals
            //  receiveSSR
            //  addPrecomputedVelocity
            //  geometricSpecularAA
            //  specularOcclusion
            //
            //  anisotropyForAreaLights
            //  recomputeStackPerLight
            //  shadeBaseUsingRefractedAngles

            // Base parametrization:

            ps.Add(new PropertyRow(CreateLabel("Base Color Parametrization", indentLevel)), (row) =>
            {
                row.Add(new EnumField(StackLit.BaseParametrization.BaseMetallic), (field) =>
                {
                    field.value = m_Node.baseParametrization;
                    field.RegisterValueChangedCallback(ChangeBaseParametrization);
                });
            });

            if (m_Node.baseParametrization == StackLit.BaseParametrization.SpecularColor)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Energy Conserving Specular", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.energyConservingSpecular.isOn;
                        toggle.OnToggleChanged(ChangeEnergyConservingSpecular);
                    });
                });
                --indentLevel;
            }

            // Material type enables:
            ps.Add(new PropertyRow(CreateLabel("Material Core Features", indentLevel)), (row) => {});
            ++indentLevel;

            ps.Add(new PropertyRow(CreateLabel("Anisotropy", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.anisotropy.isOn;
                    toggle.OnToggleChanged(ChangeAnisotropy);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Coat", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.coat.isOn;
                    toggle.OnToggleChanged(ChangeCoat);
                });
            });

            if (m_Node.coat.isOn)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Coat Normal", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.coatNormal.isOn;
                        toggle.OnToggleChanged(ChangeCoatNormal);
                    });
                });
                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Dual Specular Lobe", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.dualSpecularLobe.isOn;
                    toggle.OnToggleChanged(ChangeDualSpecularLobe);
                });
            });

            if (m_Node.dualSpecularLobe.isOn)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Dual SpecularLobe Parametrization", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLit.DualSpecularLobeParametrization.HazyGloss), (field) =>
                    {
                        field.value = m_Node.dualSpecularLobeParametrization;
                        field.RegisterValueChangedCallback(ChangeDualSpecularLobeParametrization);
                    });
                });
                if ((m_Node.baseParametrization == StackLit.BaseParametrization.BaseMetallic) &&
                    (m_Node.dualSpecularLobeParametrization == StackLit.DualSpecularLobeParametrization.HazyGloss))
                {
                    ps.Add(new PropertyRow(CreateLabel("Cap Haziness For Non Metallic", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.capHazinessWrtMetallic.isOn;
                            toggle.OnToggleChanged(ChangeCapHazinessWrtMetallic);
                        });
                    });
                }
                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Iridescence", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.iridescence.isOn;
                    toggle.OnToggleChanged(ChangeIridescence);
                });
            });

            if (m_Node.surfaceType != SurfaceType.Transparent)
            {
                ps.Add(new PropertyRow(CreateLabel("Subsurface Scattering", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.subsurfaceScattering.isOn;
                        toggle.OnToggleChanged(ChangeSubsurfaceScattering);
                    });
                });
            }

            ps.Add(new PropertyRow(CreateLabel("Transmission", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.transmission.isOn;
                    toggle.OnToggleChanged(ChangeTransmission);
                });
            });
            --indentLevel; // ...Material type enables.

            ps.Add(new PropertyRow(CreateLabel("Receive Decals", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.receiveDecals.isOn;
                    toggle.OnToggleChanged(ChangeReceiveDecals);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Receive SSR", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.receiveSSR.isOn;
                    toggle.OnToggleChanged(ChangeReceiveSSR);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Add Precomputed Velocity", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.addPrecomputedVelocity.isOn;
                    toggle.OnToggleChanged(ChangeAddPrecomputedVelocity);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Geometric Specular AA", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.geometricSpecularAA.isOn;
                    toggle.OnToggleChanged(ChangeGeometricSpecularAA);
                });
            });

            //ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (main enable)", indentLevel)), (row) =>
            //{
            //    row.Add(new Toggle(), (toggle) =>
            //    {
            //        toggle.value = m_Node.specularOcclusion.isOn;
            //        toggle.OnToggleChanged(ChangeSpecularOcclusion);
            //    });
            //});

            ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (from SSAO)", indentLevel)), (row) =>
            {
                row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionBaseMode.DirectFromAO), (field) =>
                {
                    field.value = m_Node.screenSpaceSpecularOcclusionBaseMode;
                    field.RegisterValueChangedCallback(ChangeScreenSpaceSpecularOcclusionBaseMode);
                });
            });
            if (StackLitMasterNode.SpecularOcclusionModeUsesVisibilityCone(m_Node.screenSpaceSpecularOcclusionBaseMode))
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (SS) AO Cone Weight", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionAOConeSize.CosWeightedAO), (field) =>
                    {
                        field.value = m_Node.screenSpaceSpecularOcclusionAOConeSize;
                        field.RegisterValueChangedCallback(ChangeScreenSpaceSpecularOcclusionAOConeSize);
                    });
                });
                ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (SS) AO Cone Dir", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionAOConeDir.ShadingNormal), (field) =>
                    {
                        field.value = m_Node.screenSpaceSpecularOcclusionAOConeDir;
                        field.RegisterValueChangedCallback(ChangeScreenSpaceSpecularOcclusionAOConeDir);
                    });
                });
                --indentLevel;
            }

            // TODO: we would ideally need one value per lobe
            //ps.Add(new PropertyRow(CreateLabel("Specular Occlusion Custom Input", indentLevel)), (row) =>
            //{
            //    row.Add(new Toggle(), (toggle) =>
            //    {
            //        toggle.value = m_Node.specularOcclusionIsCustom.isOn;
            //        toggle.OnToggleChanged(ChangeSpecularOcclusionIsCustom);
            //    });
            //});

            //if (!m_Node.specularOcclusionIsCustom.isOn)
            {
                ps.Add(new PropertyRow(CreateLabel("Specular Occlusion (from input AO)", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionBaseMode.DirectFromAO), (field) =>
                    {
                        field.value = m_Node.dataBasedSpecularOcclusionBaseMode;
                        field.RegisterValueChangedCallback(ChangeDataBasedSpecularOcclusionBaseMode);
                    });
                });
                if (StackLitMasterNode.SpecularOcclusionModeUsesVisibilityCone(m_Node.dataBasedSpecularOcclusionBaseMode))
                {
                    ++indentLevel;
                    ps.Add(new PropertyRow(CreateLabel("Specular Occlusion AO Cone Weight", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionAOConeSize.CosWeightedBentCorrectAO), (field) =>
                        {
                            field.value = m_Node.dataBasedSpecularOcclusionAOConeSize;
                            field.RegisterValueChangedCallback(ChangeDataBasedSpecularOcclusionAOConeSize);
                        });
                    });
                    --indentLevel;
                }
            }

            if (m_Node.SpecularOcclusionUsesBentNormal())
            {
                ps.Add(new PropertyRow(CreateLabel("Specular Occlusion Bent Cone Fixup", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLitMasterNode.SpecularOcclusionConeFixupMethod.Off), (field) =>
                    {
                        field.value = m_Node.specularOcclusionConeFixupMethod;
                        field.RegisterValueChangedCallback(ChangeSpecularOcclusionConeFixupMethod);
                    });
                });
            }

            ps.Add(new PropertyRow(CreateLabel("Advanced Options", indentLevel)), (row) => {});
            ++indentLevel;

            ps.Add(new PropertyRow(CreateLabel("Anisotropy For Area Lights", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.anisotropyForAreaLights.isOn;
                    toggle.OnToggleChanged(ChangeAnisotropyForAreaLights);
                });
            });

            if (m_Node.coat.isOn || m_Node.iridescence.isOn)
            {
                ps.Add(new PropertyRow(CreateLabel("Per Punctual/Directional Lights:", indentLevel)), (row) => { });
                ++indentLevel;
            }
            if (m_Node.coat.isOn)
            {
                ps.Add(new PropertyRow(CreateLabel("Base Layer Uses Refracted Angles", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.shadeBaseUsingRefractedAngles.isOn;
                        toggle.OnToggleChanged(ChangeShadeBaseUsingRefractedAngles);
                    });
                });
            }
            if (m_Node.coat.isOn || m_Node.iridescence.isOn)
            {
                ps.Add(new PropertyRow(CreateLabel("Recompute Stack & Iridescence", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.recomputeStackPerLight.isOn;
                        toggle.OnToggleChanged(ChangeRecomputeStackPerLight);
                    });
                });
            }
            if (m_Node.coat.isOn || m_Node.iridescence.isOn)
            {
                --indentLevel; // Per Punctual/Directional Lights:
            }

            ps.Add(new PropertyRow(CreateLabel("Show And Enable StackLit Debugs", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.debug.isOn;
                    toggle.OnToggleChanged(ChangeDebug);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Override Baked GI", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.overrideBakedGI.isOn;
                    toggle.OnToggleChanged(ChangeoverrideBakedGI);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Depth Offset", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.depthOffset.isOn;
                    toggle.OnToggleChanged(ChangeDepthOffset);
                });
            });

            --indentLevel; //...Advanced options

            Add(ps);
        }
        public StackLitSettingsView(StackLitMasterNode node)
        {
            m_Node = node;
            PropertySheet ps = new PropertySheet();

            int indentLevel = 0;

            ps.Add(new PropertyRow(CreateLabel("Surface Type", indentLevel)), (row) =>
            {
                row.Add(new EnumField(SurfaceType.Opaque), (field) =>
                {
                    field.value = m_Node.surfaceType;
                    field.OnValueChanged(ChangeSurfaceType);
                });
            });

            if (m_Node.surfaceType == SurfaceType.Transparent)
            {
                ++indentLevel;

                // No refraction in StackLit, always show this:
                ps.Add(new PropertyRow(CreateLabel("Blending Mode", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLitMasterNode.AlphaModeLit.Additive), (field) =>
                    {
                        field.value = GetAlphaModeLit(m_Node.alphaMode);
                        field.OnValueChanged(ChangeBlendMode);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Blend Preserves Specular", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.blendPreserveSpecular.isOn;
                        toggle.OnToggleChanged(ChangeBlendPreserveSpecular);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Fog", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.transparencyFog.isOn;
                        toggle.OnToggleChanged(ChangeTransparencyFog);
                    });
                });

                ps.Add(new PropertyRow(CreateLabel("Distortion", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.distortion.isOn;
                        toggle.OnToggleChanged(ChangeDistortion);
                    });
                });

                if (m_Node.distortion.isOn)
                {
                    ++indentLevel;
                    ps.Add(new PropertyRow(CreateLabel("Mode", indentLevel)), (row) =>
                    {
                        row.Add(new EnumField(DistortionMode.Add), (field) =>
                        {
                            field.value = m_Node.distortionMode;
                            field.OnValueChanged(ChangeDistortionMode);
                        });
                    });
                    ps.Add(new PropertyRow(CreateLabel("Depth Test", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.distortionDepthTest.isOn;
                            toggle.OnToggleChanged(ChangeDistortionDepthTest);
                        });
                    });
                    --indentLevel;
                }

                m_SortPiorityField = new IntegerField();
                ps.Add(new PropertyRow(CreateLabel("Sort Priority", indentLevel)), (row) =>
                {
                    row.Add(m_SortPiorityField, (field) =>
                    {
                        field.value = m_Node.sortPriority;
                        field.OnValueChanged(ChangeSortPriority);
                    });
                });
                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Alpha Cutoff", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.alphaTest.isOn;
                    toggle.OnToggleChanged(ChangeAlphaTest);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Double Sided", indentLevel)), (row) =>
            {
                row.Add(new EnumField(DoubleSidedMode.Disabled), (field) =>
                {
                    field.value = m_Node.doubleSidedMode;
                    field.OnValueChanged(ChangeDoubleSidedMode);
                });
            });

            // Rest of UI looks like this:
            //
            //  baseParametrization
            //    energyConservingSpecular
            //
            //  anisotropy
            //  coat
            //  coatNormal
            //  dualSpecularLobe
            //    dualSpecularLobeParametrization
            //    capHazinessWrtMetallic
            //  iridescence
            //  subsurfaceScattering
            //  transmission
            //
            //  receiveDecals
            //  receiveSSR
            //  geometricSpecularAA
            //  specularOcclusion
            //
            //  anisotropyForAreaLights
            //  recomputeStackPerLight
            //  shadeBaseUsingRefractedAngles

            // Base parametrization:

            ps.Add(new PropertyRow(CreateLabel("Base Color Parametrization", indentLevel)), (row) =>
            {
                row.Add(new EnumField(StackLit.BaseParametrization.BaseMetallic), (field) =>
                {
                    field.value = m_Node.baseParametrization;
                    field.OnValueChanged(ChangeBaseParametrization);
                });
            });

            if (m_Node.baseParametrization == StackLit.BaseParametrization.SpecularColor)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Energy Conserving Specular", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.energyConservingSpecular.isOn;
                        toggle.OnToggleChanged(ChangeEnergyConservingSpecular);
                    });
                });
                --indentLevel;
            }

            // Material type enables:
            ps.Add(new PropertyRow(CreateLabel("Material Core Features", indentLevel)), (row) => {});
            ++indentLevel;

            ps.Add(new PropertyRow(CreateLabel("Anisotropy", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.anisotropy.isOn;
                    toggle.OnToggleChanged(ChangeAnisotropy);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Coat", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.coat.isOn;
                    toggle.OnToggleChanged(ChangeCoat);
                });
            });

            if (m_Node.coat.isOn)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Coat Normal", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.coatNormal.isOn;
                        toggle.OnToggleChanged(ChangeCoatNormal);
                    });
                });
                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Dual Specular Lobe", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.dualSpecularLobe.isOn;
                    toggle.OnToggleChanged(ChangeDualSpecularLobe);
                });
            });

            if (m_Node.dualSpecularLobe.isOn)
            {
                ++indentLevel;
                ps.Add(new PropertyRow(CreateLabel("Dual SpecularLobe Parametrization", indentLevel)), (row) =>
                {
                    row.Add(new EnumField(StackLit.DualSpecularLobeParametrization.HazyGloss), (field) =>
                    {
                        field.value = m_Node.dualSpecularLobeParametrization;
                        field.OnValueChanged(ChangeDualSpecularLobeParametrization);
                    });
                });
                if ((m_Node.baseParametrization == StackLit.BaseParametrization.BaseMetallic) &&
                    (m_Node.dualSpecularLobeParametrization == StackLit.DualSpecularLobeParametrization.HazyGloss))
                {
                    ps.Add(new PropertyRow(CreateLabel("Cap Haziness For Non Metallic", indentLevel)), (row) =>
                    {
                        row.Add(new Toggle(), (toggle) =>
                        {
                            toggle.value = m_Node.capHazinessWrtMetallic.isOn;
                            toggle.OnToggleChanged(ChangeCapHazinessWrtMetallic);
                        });
                    });
                }
                --indentLevel;
            }

            ps.Add(new PropertyRow(CreateLabel("Iridescence", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.iridescence.isOn;
                    toggle.OnToggleChanged(ChangeIridescence);
                });
            });

            if (m_Node.surfaceType != SurfaceType.Transparent)
            {
                ps.Add(new PropertyRow(CreateLabel("Subsurface Scattering", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.subsurfaceScattering.isOn;
                        toggle.OnToggleChanged(ChangeSubsurfaceScattering);
                    });
                });
            }

            ps.Add(new PropertyRow(CreateLabel("Transmission", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.transmission.isOn;
                    toggle.OnToggleChanged(ChangeTransmission);
                });
            });
            --indentLevel; // ...Material type enables.

            ps.Add(new PropertyRow(CreateLabel("Receive Decals", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.receiveDecals.isOn;
                    toggle.OnToggleChanged(ChangeReceiveDecals);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Receive SSR", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.receiveSSR.isOn;
                    toggle.OnToggleChanged(ChangeReceiveSSR);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Specular AA (for geometry)", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.geometricSpecularAA.isOn;
                    toggle.OnToggleChanged(ChangeGeometricSpecularAA);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Specular Occlusion", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.specularOcclusion.isOn;
                    toggle.OnToggleChanged(ChangeSpecularOcclusion);
                });
            });

            ps.Add(new PropertyRow(CreateLabel("Advanced Options", indentLevel)), (row) => {});
            ++indentLevel;

            ps.Add(new PropertyRow(CreateLabel("Anisotropy For Area Lights", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.anisotropyForAreaLights.isOn;
                    toggle.OnToggleChanged(ChangeAnisotropyForAreaLights);
                });
            });

            if (m_Node.coat.isOn || m_Node.iridescence.isOn)
            {
                ps.Add(new PropertyRow(CreateLabel("Per Punctual/Directional Lights:", indentLevel)), (row) => { });
                ++indentLevel;
            }
            if (m_Node.coat.isOn)
            {
                ps.Add(new PropertyRow(CreateLabel("Base Layer Uses Refracted Angles", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.shadeBaseUsingRefractedAngles.isOn;
                        toggle.OnToggleChanged(ChangeShadeBaseUsingRefractedAngles);
                    });
                });
            }
            if (m_Node.coat.isOn || m_Node.iridescence.isOn)
            {
                ps.Add(new PropertyRow(CreateLabel("Recompute Stack & Iridescence", indentLevel)), (row) =>
                {
                    row.Add(new Toggle(), (toggle) =>
                    {
                        toggle.value = m_Node.recomputeStackPerLight.isOn;
                        toggle.OnToggleChanged(ChangeRecomputeStackPerLight);
                    });
                });
            }
            if (m_Node.coat.isOn || m_Node.iridescence.isOn)
            {
                --indentLevel; // Per Punctual/Directional Lights:
            }

            ps.Add(new PropertyRow(CreateLabel("Show And Enable StackLit Debugs", indentLevel)), (row) =>
            {
                row.Add(new Toggle(), (toggle) =>
                {
                    toggle.value = m_Node.debug.isOn;
                    toggle.OnToggleChanged(ChangeDebug);
                });
            });

            --indentLevel; //...Advanced options

            Add(ps);
        }