Shader "Hidden/Light2D"
{
    Properties
    {
        [HideInInspector] _SrcBlend("__src", Float) = 1.0
        [HideInInspector] _DstBlend("__dst", Float) = 0.0
    }

    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            Blend [_SrcBlend][_DstBlend]
            ZWrite Off
            ZTest Off
            Cull Off

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_local USE_NORMAL_MAP __
            #pragma multi_compile_local USE_ADDITIVE_BLENDING __
            #pragma multi_compile_local USE_VOLUMETRIC __
            #pragma multi_compile_local USE_POINT_LIGHT_COOKIES __
            #pragma multi_compile_local LIGHT_QUALITY_FAST __

            #include_with_pragmas "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/ShapeLightShared.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/LightingUtility.hlsl"

            struct Attributes
            {
                float3 positionOS   : POSITION;
                float4 color        : COLOR;
                float2 uv           : TEXCOORD0;
            };

            struct Varyings
            {
                float4  positionCS  : SV_POSITION;
                half4   color       : COLOR;
                half2   uv          : TEXCOORD0;
                half2   lookupUV    : TEXCOORD1;  // This is used for light relative direction

                SHADOW_COORDS(TEXCOORD2)
                NORMALS_LIGHTING_COORDS(TEXCOORD3, TEXCOORD4)
                LIGHT_OFFSET(TEXCOORD5)
            };

            TEXTURE2D(_CookieTex);          // This can either be a sprite texture uv or a falloff texture
            SAMPLER(sampler_CookieTex);

            TEXTURE2D(_FalloffLookup);
            SAMPLER(sampler_FalloffLookup);

            TEXTURE2D(_LightLookup);
            SAMPLER(sampler_LightLookup);
            half4 _LightLookup_TexelSize;

#if USE_POINT_LIGHT_COOKIES
            TEXTURE2D(_PointLightCookieTex);
            SAMPLER(sampler_PointLightCookieTex);
#endif

            NORMALS_LIGHTING_VARIABLES
            SHADOW_VARIABLES
            UNITY_LIGHT2D_DATA

            half _InverseHDREmulationScale;

            Varyings vert_shape(Attributes a, PerLight2D light)
            {
                Varyings o = (Varyings)0;
                float3 positionOS = a.positionOS;

                positionOS.x = positionOS.x + _L2D_FALLOFF_DISTANCE * a.color.r;
                positionOS.y = positionOS.y + _L2D_FALLOFF_DISTANCE * a.color.g;

                o.positionCS = TransformObjectToHClip(positionOS);
                o.color = _L2D_COLOR * _InverseHDREmulationScale;
                o.color.a = a.color.a;
#if USE_VOLUMETRIC
                o.color.a = _L2D_COLOR.a * _L2D_VOLUME_OPACITY;
#endif

                // If Sprite use UV.
                o.uv = (_L2D_LIGHT_TYPE == 2) ? a.uv : float2(a.color.a, _L2D_FALLOFF_INTENSITY);

                float4 worldSpacePos;
                worldSpacePos.xyz = TransformObjectToWorld(positionOS);
                worldSpacePos.w = 1;
                TRANSFER_NORMALS_LIGHTING(o, worldSpacePos, _L2D_POSITION.xyz, _L2D_POSITION.w)
                TRANSFER_SHADOWS(o)
                return o;
            }

            Varyings vert_point(Attributes a, PerLight2D light)
            {
                Varyings output = (Varyings)0;
                output.positionCS = TransformObjectToHClip(a.positionOS);
                output.uv = a.uv;

                float4 worldSpacePos;
                worldSpacePos.xyz = TransformObjectToWorld(a.positionOS);
                worldSpacePos.w = 1;

                float4 lightSpacePos = mul(_L2D_INVMATRIX, worldSpacePos);
                float halfTexelOffset = 0.5 * _LightLookup_TexelSize.x;
                output.lookupUV = 0.5 * (lightSpacePos.xy + 1) + halfTexelOffset;

                TRANSFER_NORMALS_LIGHTING(output, worldSpacePos, _L2D_POSITION.xyz, _L2D_POSITION.w)
                TRANSFER_SHADOWS(output)
                return output;
            }

            Varyings vert(Attributes attributes)
            {

                PerLight2D light;
#if USE_STRUCTURED_BUFFER_FOR_LIGHT2D_DATA
                light = GetPerLight2D(attributes.color);
#endif

                switch (_L2D_LIGHT_TYPE)
                {
                    case 0:
                    case 1:
                    case 2:
                    {
                        Varyings v = vert_shape(attributes, light);
                        v.lightOffset = attributes.color;
                        return v;
                    }
                    break;
                    case 3:
                    {
                        Varyings v = vert_point(attributes, light);
                        v.lightOffset = attributes.color;
                        return v;
                    }
                }

                Varyings v = (Varyings)0;
                return v;
            }

            FragmentOutput frag_shape(Varyings i, PerLight2D light)
            {
                half4 lightColor = i.color;
                if (_L2D_LIGHT_TYPE == 2)
                {
                    half4 cookie = SAMPLE_TEXTURE2D(_CookieTex, sampler_CookieTex, i.uv);
#if USE_ADDITIVE_BLENDING
                    lightColor *= cookie * cookie.a;
#else
                    lightColor *= cookie;
#endif
                }
                else
                {
#if USE_ADDITIVE_BLENDING
                    lightColor *= SAMPLE_TEXTURE2D(_FalloffLookup, sampler_FalloffLookup, i.uv).r;
#elif USE_VOLUMETRIC
                    lightColor.a = i.color.a * SAMPLE_TEXTURE2D(_FalloffLookup, sampler_FalloffLookup, i.uv).r;
#else
                    lightColor.a = SAMPLE_TEXTURE2D(_FalloffLookup, sampler_FalloffLookup, i.uv).r;
#endif
                }

#if !USE_VOLUMETRIC
                APPLY_NORMALS_LIGHTING(i, lightColor, _L2D_POSITION.xyz, _L2D_POSITION.w);
#endif
                APPLY_SHADOWS(i, lightColor, _L2D_SHADOW_INTENSITY);
                return ToFragmentOutput(lightColor);
            }

            FragmentOutput frag_point(Varyings i, PerLight2D light)
            {
                half4 lookupValue = SAMPLE_TEXTURE2D(_LightLookup, sampler_LightLookup, i.lookupUV);  // r = distance, g = angle, b = x direction, a = y direction

                // Inner Radius
                half attenuation = saturate(_L2D_INNER_RADIUS_MULT * lookupValue.r);   // This is the code to take care of our inner radius

                // Spotlight
                half isFullSpotlight = _L2D_INNER_ANGLE == 1.0f;
                half spotAttenuation = saturate((_L2D_OUTER_ANGLE - lookupValue.g + isFullSpotlight) * (1.0f / (_L2D_OUTER_ANGLE - _L2D_INNER_ANGLE)));
                attenuation = attenuation * spotAttenuation;

                half2 mappedUV;
                mappedUV.x = attenuation;
                mappedUV.y = _L2D_FALLOFF_INTENSITY;
                attenuation = SAMPLE_TEXTURE2D(_FalloffLookup, sampler_FalloffLookup, mappedUV).r;

                half4 lightColor = _L2D_COLOR;
#if USE_POINT_LIGHT_COOKIES
                half4 cookieColor = SAMPLE_TEXTURE2D(_PointLightCookieTex, sampler_PointLightCookieTex, i.lookupUV);
                lightColor = cookieColor * _L2D_COLOR;
#endif

#if USE_ADDITIVE_BLENDING || USE_VOLUMETRIC
                lightColor *= attenuation;
#else
                lightColor.a = attenuation;
#endif

#if !USE_VOLUMETRIC
                APPLY_NORMALS_LIGHTING(i, lightColor, _L2D_POSITION.xyz, _L2D_POSITION.w);
#endif
                APPLY_SHADOWS(i, lightColor, _L2D_SHADOW_INTENSITY);

#if USE_VOLUMETRIC
                lightColor *= _L2D_VOLUME_OPACITY;
#endif

                return ToFragmentOutput(lightColor * _InverseHDREmulationScale);
            }

            FragmentOutput frag(Varyings i)
            {

                PerLight2D light;
#if USE_STRUCTURED_BUFFER_FOR_LIGHT2D_DATA
                light = GetPerLight2D(i.lightOffset);
#endif

                switch (_L2D_LIGHT_TYPE)
                {
                    case 0:
                    case 1:
                    case 2:
                    {
                        FragmentOutput output = frag_shape(i, light);
                        return output;
                    }
                    break;
                    case 3:
                    {
                        FragmentOutput output = frag_point(i, light);
                        return output;
                    }
                }

                half4 color = i.color;
                return ToFragmentOutput(color);
            }
            ENDHLSL
        }
    }
}