////////////////////////////////////////////////
//
// deferred shading
//
////////////////////////////////////////////////

msaadetectedges = [
    result [{
        @(if (glext "GL_EXT_shader_samples_identical") [result [
            if(textureSamplesIdenticalEXT(tex1, ivec2(gl_FragCoord.xy)))
            {
                @arg1
            }
            else 
        ]])

        { 
            vec4 e = texelFetch(tex1, ivec2(gl_FragCoord.xy), 0);
            e.xyz -= 0.5;
            float maxdiff = 0.98*0.98*dot(e.xyz, e.xyz); 

            @(loopconcat+ i 1 (- $msaasamples 1) [result [
                vec4 e@i = texelFetch(tex1, ivec2(gl_FragCoord.xy), @i);
                e@i.xyz -= 0.5;
                if(abs(e.w-e@i.w) <= 2.0/255.0 && pow(dot(e@i.xyz, e.xyz), 2.0) >= maxdiff*dot(e@i.xyz, e@i.xyz))
                {
            ]])

            @arg1

            @(loopconcat+ i 1 (- $msaasamples 1) [result [
                }
            ]])
        }
    }]
]

lazyshader 0 msaaedgedetect [
    in vec4 vvertex;
    void main(void)
    {
        gl_Position = vvertex;
    }
] [
    @(? (glext "GL_EXT_shader_samples_identical") [
        #extension GL_EXT_shader_samples_identical : enable
    ])
    uniform sampler2DMS tex1;

    void main(void)
    {
        @(msaadetectedges [discard;])
    }
]

// deferredlighttype:
//    p -> point-light shadow (default cubemap)
//    c -> CSM
//    a -> AO
//    A -> AO sun
//    r -> radiance hints
//    G -> 5x5 weighted gather filter
//    g -> 3x3 weighted gather filter
//    E -> 5x5 weighted bilinear filter
//    F -> 3x3 weighted bilinear filter
//    f -> 4x rotated grid filter
//    m -> minimap
//    M -> multisampled
//    O -> sample 1
//    R -> manual resolve
//    S -> sample shading
//    T -> edge detection
//    n -> tile batching
//    s -> spotlights
//    t -> transparent
//    b -> combined base/light variants
//    d -> avatar shadow dist bias variants
//    D -> disable dist bias
//    z -> spec toggle
dlopt = [ >= (strstr $deferredlighttype $arg1) 0 ]

unpacknormbias = 0.005
unpacknormscale = (+f 1 (*f 2 $unpacknormbias))
unpacknorm = [
    result [
        @arg1 = clamp(@arg1 * @(divf $unpacknormscale 0.5) - @(+f (*f $unpacknormscale (divf 0.25 0.5)) $unpacknormbias), 0.0, 1.0);
    ]
]

unpackspec = [
    result [
        vec3 camdir = normalize(camera - pos.xyz);
        float facing = 2.0*dot(normal.xyz, camdir);
        float specscale = min(3.0*(diffuse.a + 0.5/255.0), 2.999), gloss = floor(specscale);
        specscale -= gloss;
        specscale = (0.35 + 0.15*gloss) * specscale / (1.5 - specscale);
        gloss = 5.0 + 17.0*gloss;
    ]
]

unpackdistbias = [
    cond [$avatar] [result [
        #define distbias -@avatarshadowbias
    ]] [|| $transparent [dlopt "d"] [dlopt "D"] [dlopt "m"]] [result [
        #define distbias 0.0
    ]] [result [
        float distbias = -@avatarshadowbias * @(? (dlopt "a") [avatarmask] [step(fogcoord, @avatarshadowdist) * @(? $msaasamples [step(0.75, normal.a)] [normal.a])]);
    ]]
]

deferredlightvariantshader = [
    local deferredlighttype
    deferredlighttype = $arg3
    numsplits = (+ $arg4 0)
    numrh = (+ $arg5 0)
    numlights = (+ $arg6 0)
    baselight = (< (mod $arg2 4) 2)
    spotlight = (>= (mod $arg2 8) 4) 
    transparent = (<= 8 $arg2 16)
    avatar = (<= 17 $arg2 31)
    variantshader 0 $arg1 $arg2 (? (< $arg2 0) [
        in vec4 vvertex;
        uniform mat4 lightmatrix;
        void main(void)
        {
            gl_Position = lightmatrix * vvertex;
        }
    ]) [
        @(? (&& (dlopt "T") [glext "GL_EXT_shader_samples_identical"]) [
            #extension GL_EXT_shader_samples_identical : enable
        ])
        @(if (dlopt "M") [result [
            uniform sampler2DMS tex0, tex1, tex3 @(? $transparent [, tex2]);
        ]] [result [
            uniform sampler2DRect tex0, tex1, tex3 @(? $transparent [, tex2]);
        ]])
        @(if (|| (dlopt "p") (dlopt "c")) [
            if (|| (dlopt "g") (dlopt "G")) [if (> $usetexgather 1) [result [
                uniform sampler2DShadow tex4;
            ]] [result [
                uniform sampler2D tex4;
            ]]] [result [
                uniform sampler2DRectShadow tex4;
            ]]
        ])
        @(if $numlights [result [
            uniform vec4 lightpos[@@numlights];
            uniform vec4 lightcolor[@@numlights];
            @(if $spotlight [result [
                uniform vec4 spotparams[@@numlights];
            ]])
            @(if (dlopt "p") [result [
                uniform vec4 shadowparams[@@numlights];
                uniform vec2 shadowoffset[@@numlights];
            ]])
        ]])
        @(if $numsplits [result [
            uniform vec4 csmtc[@@numsplits];
            uniform vec3 csmoffset[@@numsplits];
            uniform vec2 csmz;
        ]])
        @(if (dlopt "c") [result [
            uniform mat3 csmmatrix;
            uniform vec3 sunlightdir;
            uniform vec3 sunlightcolor;
            @(if (dlopt "r") [result [
                uniform vec3 skylightcolor;
                uniform float giscale, rhnudge, rhbounds;
                uniform vec4 rhtc[@@numrh];
                uniform sampler3D tex6, tex7, tex8, tex9;
            ]])
        ]])
        uniform vec3 camera;
        uniform mat4 worldmatrix;
        uniform vec4 fogdir;
        uniform vec3 fogcolor;
        uniform vec2 fogdensity;
        uniform vec4 radialfogscale;
        uniform vec2 shadowatlasscale;
        uniform vec4 lightscale;
        @(? (&& (dlopt "a") [! $avatar] [! $transparent]) [uniform sampler2DRect tex5; uniform vec2 aoscale; uniform vec4 aoparams;])
        @(gdepthunpackparams)
        layout(location = 0) out vec4 fragcolor;

        @(if (dlopt "p") [
            ? $spotlight [
                vec3 getspottc(vec3 dir, float spotdist, vec4 spotparams, vec4 shadowparams, vec2 shadowoffset, float distbias)
                {
                    vec2 mparams = shadowparams.xy / max(spotdist + distbias, 1e-5);
                    return vec3((dir.xy - spotparams.xy*(spotdist + (spotparams.z > 0.0 ? 1.0 : -1.0)*dir.z)*shadowparams.z) * mparams.x + shadowoffset, mparams.y + shadowparams.w);
                }
            ] [
                vec3 getshadowtc(vec3 dir, vec4 shadowparams, vec2 shadowoffset, float distbias)
                {
                    vec3 adir = abs(dir);
                    float m = max(adir.x, adir.y), mz = max(adir.z, m);
                    vec2 mparams = shadowparams.xy / max(mz + distbias, 1e-5);
                    vec4 proj;
                    if(adir.x > adir.y) proj = vec4(dir.zyx, 0.0); else proj = vec4(dir.xzy, 1.0);
                    if(adir.z > m) proj = vec4(dir, 2.0);
                    return vec3(proj.xy * mparams.x + vec2(proj.w, step(0.0, proj.z)) * shadowparams.z + shadowoffset, mparams.y + shadowparams.w);
                }
            ]
        ])

        @(if (|| (dlopt "p") [dlopt "c"]) [
            cond [dlopt "G"] [result [
                @(? (> $usetexgather 1) [
                #define shadowgather(center, xoff, yoff) textureGatherOffset(tex4, center, shadowtc.z, ivec2(xoff, yoff))
                ] [
                #define shadowgather(center, xoff, yoff) step(shadowtc.z, textureGatherOffset(tex4, center, ivec2(xoff, yoff)))
                ])
                float filtershadow(vec3 shadowtc)
                {
                    vec2 offset = fract(shadowtc.xy - 0.5), center = (shadowtc.xy - offset)*shadowatlasscale;
                    vec4 group1 = shadowgather(center, -2, -2);
                    vec4 group2 = shadowgather(center,  0, -2);
                    vec4 group3 = shadowgather(center,  2, -2);
                    vec4 group4 = shadowgather(center, -2,  0);
                    vec4 group5 = shadowgather(center,  0,  0);
                    vec4 group6 = shadowgather(center,  2,  0);
                    vec4 group7 = shadowgather(center, -2,  2);
                    vec4 group8 = shadowgather(center,  0,  2);
                    vec4 group9 = shadowgather(center,  2,  2);
                    vec4 locols = vec4(group1.ab, group3.ab);
                    vec4 hicols = vec4(group7.rg, group9.rg);
                    locols.yz += group2.ab;
                    hicols.yz += group8.rg;
                    vec4 midcols = vec4(group1.rg, group3.rg) + vec4(group7.ab, group9.ab) +
                                   vec4(group4.rg, group6.rg) + vec4(group4.ab, group6.ab) +
                                   mix(locols, hicols, offset.y);
                    vec4 cols = group5 + vec4(group2.rg, group8.ab);
                    cols.xyz += mix(midcols.xyz, midcols.yzw, offset.x);
                    return dot(cols, vec4(1.0/25.0));
                }
            ]] [dlopt "g"] [result [
                @(? (> $usetexgather 1) [
                #define shadowgather(center, xoff, yoff) textureGatherOffset(tex4, center, shadowtc.z, ivec2(xoff, yoff))
                ] [
                #define shadowgather(center, xoff, yoff) step(shadowtc.z, textureGatherOffset(tex4, center, ivec2(xoff, yoff)))
                ])
                float filtershadow(vec3 shadowtc)
                {
                    vec2 offset = fract(shadowtc.xy - 0.5), center = (shadowtc.xy - offset)*shadowatlasscale;
                    vec4 group1 = shadowgather(center, -1, -1);
                    vec4 group2 = shadowgather(center,  1, -1);
                    vec4 group3 = shadowgather(center, -1,  1);
                    vec4 group4 = shadowgather(center,  1,  1);
                    vec4 cols = vec4(group1.rg, group2.rg) + vec4(group3.ab, group4.ab) + mix(vec4(group1.ab, group2.ab), vec4(group3.rg, group4.rg), offset.y);
                    return dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0));
                }
            ]] [dlopt "E"] [result [
                #define shadowval(xy, xoff, yoff) float(textureRect(tex4, vec3(xy + vec2(xoff, yoff), shadowtc.z)))
                float filtershadow(vec3 shadowtc)
                {
                    vec2 offset = fract(shadowtc.xy - 0.5);
                    vec4 center = vec4(shadowtc.xy - offset + 0.5, shadowtc.xy - offset*0.5);
                    vec4 size = vec4(offset + 1.0, 2.0 - offset);
                    return (1.0/25.0)*dot(size.zxzx*size.wwyy,
                            vec4(shadowval(center.zw, -1.5, -1.5),
                                 shadowval(center.zw, 2.0, -1.5),
                                 shadowval(center.zw, -1.5, 2.0),
                                 shadowval(center.zw, 2.0, 2.0))) +
                           (2.0/25.0)*dot(size,
                            vec4(shadowval(center.zy, 2.0, 0.0),
                                 shadowval(center.xw, 0.0, 2.0),
                                 shadowval(center.zy, -1.5, 0.0),
                                 shadowval(center.xw, 0.0, -1.5))) +
                           (4.0/25.0)*shadowval(center.xy, 0.0, 0.0);
                }
            ]] [dlopt "F"] [result [
                #define shadowval(center, xoff, yoff) float(textureRect(tex4, center + vec3(xoff, yoff, 0.0)))
                float filtershadow(vec3 shadowtc)
                {
                    vec2 offset = fract(shadowtc.xy - 0.5);
                    vec3 center = shadowtc;
                    //center.xy -= offset;
                    //vec4 size = vec4(offset + 1.0, 2.0 - offset), weight = vec4(2.0 - 1.0 / size.xy, 1.0 / size.zw - 1.0);
                    //return (1.0/9.0)*dot(size.zxzx*size.wwyy,
                    //    vec4(shadowval(center, weight.zw),
                    //         shadowval(center, weight.xw),
                    //         shadowval(center, weight.zy),
                    //         shadowval(center, weight.xy)));
                    center.xy -= offset*0.5;
                    vec4 size = vec4(offset + 1.0, 2.0 - offset);
                    return (1.0/9.0)*dot(size.zxzx*size.wwyy,
                        vec4(shadowval(center, -0.5, -0.5),
                             shadowval(center, 1.0, -0.5),
                             shadowval(center, -0.5, 1.0),
                             shadowval(center, 1.0, 1.0)));
                }
            ]] [dlopt "f"] [result [
                #define shadowval(center, xoff, yoff) float(textureRect(tex4, center + vec3(xoff, yoff, 0.0)))
                float filtershadow(vec3 shadowtc)
                {
                    return dot(vec4(0.25),
                                vec4(shadowval(shadowtc, -0.4, 1.0),
                                    shadowval(shadowtc, -1.0, -0.4),
                                    shadowval(shadowtc, 0.4, -1.0),
                                    shadowval(shadowtc, 1.0, 0.4))); 
                }
            ]] [result [
                #define filtershadow(shadowtc) float(textureRect(tex4, shadowtc))
            ]]
        ])

        @(if (dlopt "c") [result [
            vec3 getcsmtc(vec3 pos, float distbias)
            {
                vec3 tc, offset;
                pos = csmmatrix * pos;
                pos.z -= distbias;
                tc.z = csmz.x + pos.z*csmz.y; 
                @(loopconcat j $numsplits [result [
                    tc.xy = csmtc[@@j].xy + pos.xy*csmtc[@@j].z;
                    offset = csmoffset[@@j];
                    if(max(abs(tc.x), abs(tc.y)) >= csmtc[@@j].w)
                    {
                ]])
                        offset = vec3(-16384.0);
                @(loopconcat j $numsplits [result [
                    }
                ]])
                return tc + offset;
            }

            @(if (dlopt "r") [result [
                vec4 getrhlight(vec3 pos, vec3 norm)
                {
                    vec3 tc;
                    float offset;
                    pos += norm*rhnudge;
                    @(loopconcat j $numrh [result [
                        tc = rhtc[@@j].xyz + pos*rhtc[@@j].w;
                        offset = @(divf (+f 0.5 $j) $numrh);
                        if(max(max(abs(tc.x), abs(tc.y)), abs(tc.z)) >= rhbounds)
                        {
                    ]])
                            tc = vec3(4.0);
                    @(loopconcat j $numrh [result [
                        }
                    ]])
                    tc.xy += 0.5;
                    tc.z = tc.z * @(divf 1 $numrh) + offset;
                    vec4 shr = texture(tex6, tc), shg = texture(tex7, tc), shb = texture(tex8, tc), sha = texture(tex9, tc);
                    shr.rgb -= 0.5;
                    shg.rgb -= 0.5;
                    shb.rgb -= 0.5;
                    sha.rgb -= 0.5;
                    vec4 basis = vec4(norm*-(1.023326*0.488603/3.14159*2.0), (0.886226*0.282095/3.14159));
                    return clamp(vec4(dot(basis, shr), dot(basis, shg), dot(basis, shb), min(dot(basis, sha), norm.z + 1.0)), 0.0, 1.0);
                }
            ]])
        ]])

        void main(void)
        {
            @(if (dlopt "M") [
                if (dlopt "R") [result [
                    @(if (dlopt "T") [result [
                        bool shouldresolve = true;
                        @(msaadetectedges [shouldresolve = false;])
                    ]])

                    #define gfetch(sampler, coords) texelFetch(sampler, ivec2(coords), sampleidx)

                    vec4 resolved = vec4(0.0);
                    #define accumlight(light) resolved.rgb += light
                    #define accumalpha(alpha) resolved.a += alpha

                    @(if (&& [dlopt "a"] [! $avatar] [! $transparent]) [result [
                        float ao = textureRect(tex5, gl_FragCoord.xy*aoscale).r;
                    ]])

                    for(int sampleidx = 0; sampleidx < @msaasamples; sampleidx++) 
                    {
                ]] [result [
                    @(if (dlopt "T") [msaadetectedges [discard;]])

                    #define gfetch(sampler, coords) texelFetch(sampler, ivec2(coords), @(? (dlopt "S") [gl_SampleID] (? (dlopt "O") 1 0)))

                    #define accumlight(light) fragcolor.rgb = light
                    #define accumalpha(alpha) fragcolor.a = alpha
                ]]
            ] [result [
                #define gfetch(sampler, coords) textureRect(sampler, coords)

                #define accumlight(light) fragcolor.rgb = light
                #define accumalpha(alpha) fragcolor.a = alpha
            ]])

            @(if (|| $baselight [> $numlights 1]) [result [
                vec4 normal = gfetch(tex1, gl_FragCoord.xy);

                @(if $transparent [result [
                    @(? (! $ghasstencil) [
                        if(normal.x + normal.y == 0.0) discard;
                    ])

                    normal.xyz = normal.xyz*2.0 - 1.0;
                    @(if $usepacknorm [result [
                        float alpha = dot(normal.xyz, normal.xyz);
                        normal.xyz *= inversesqrt(alpha);
                        @(if $baselight [unpacknorm alpha])
                    ]] [result [
                        #define alpha normal.a
                    ]])

                    vec4 diffuse = gfetch(tex0, gl_FragCoord.xy);
                    @(? $baselight [
                        vec3 glow = gfetch(tex2, gl_FragCoord.xy).rgb;
                    ])
                ]] [result [
                    @(? $baselight [
                        float alpha = float(normal.x + normal.y != 0.0);
                    ] [
                        #define alpha 1.0
                    ])

                    normal.xyz = normal.xyz*2.0 - 1.0;
                    @(if $usepacknorm [result [
                        float glowscale = dot(normal.xyz, normal.xyz);
                        normal.xyz *= inversesqrt(glowscale);
                        @(unpacknorm glowscale)
                    ]] [result [
                        #define glowscale normal.a
                    ]])

                    vec4 diffuse = gfetch(tex0, gl_FragCoord.xy);
                    @(? $baselight [
                        vec3 glow = diffuse.rgb * (1.0 - glowscale);
                    ])
                    diffuse.rgb *= glowscale;
                ]])
            ]])

            @(if (dlopt "m") [if (+ $numlights (dlopt "c")) [
                gdepthunpackortho depth [gfetch(tex3, gl_FragCoord.xy)] [
                    vec3 pos = (worldmatrix * vec4(gl_FragCoord.xy, depth, 1.0)).xyz;
                ]
            ]] [if (+ $numlights (dlopt "c")) [result [
                @(gdepthunpack depth [gfetch(tex3, gl_FragCoord.xy)] [
                    vec3 pos = (worldmatrix * vec4(depth*gl_FragCoord.xy, depth, 1.0)).xyz;
                ] [
                    vec4 pos = worldmatrix * vec4(gl_FragCoord.xy, depth, 1.0);
                    pos.xyz /= pos.w;
                ])
                @(? (|| $baselight [> $numlights 1]) [
                    float fogcoord = length(camera - pos.xyz);
                ])
                @(if (> (+ $numlights (dlopt "c")) 1) [unpackspec])
            ]] [result [
                @(gdepthunpack depth [gfetch(tex3, gl_FragCoord.xy)])
                @(? $baselight [
                    float fogcoord = -depth*length(vec3(gl_FragCoord.xy*radialfogscale.xy + radialfogscale.zw, 1.0));
                ])
            ]]])
           
            @(if $baselight [result [
                vec3 light = lightscale.rgb;
                @(? (dlopt "r") [
                    vec4 rhlight = getrhlight(pos.xyz, normal.xyz); 
                    light += rhlight.a * skylightcolor;
                ])
                light *= diffuse.rgb;

                @(if (&& (dlopt "a") [! $avatar] [! $transparent]) [result [
                    @(? (! (&& (dlopt "M") [dlopt "R"])) [
                        float ao = textureRect(tex5, gl_FragCoord.xy*aoscale).r;
                    ])
                    @(if (dlopt "d") [result [
                        #define aomask ao
                    ]] [result [
                        float avatarmask = step(fogcoord, @avatarshadowdist) * @(? $msaasamples [step(0.75, normal.a)] [normal.a]);
                        float aomask = clamp(ao + avatarmask, 0.0, 1.0);
                    ]])
                    light *= aoparams.x + aomask*aoparams.y;
                ]])
                light += glow * lightscale.a;
            ]] [result [
                vec3 light = vec3(0.0);
            ]])

            @(if (> (+ (? (dlopt "p") $numlights) (dlopt "c")) 1) [unpackdistbias])

            @(if (dlopt "c") [result [
                @(? (dlopt "r") [
                    vec3 sunlight = rhlight.rgb * giscale * diffuse.rgb;
                ])
                float sunfacing = dot(sunlightdir, normal.xyz);
                if(sunfacing > 0.0)
                {
                    @(if (= (+ (? (dlopt "p") $numlights) (dlopt "c")) 1) [unpackdistbias])
                    vec3 csmtc = getcsmtc(pos.xyz, distbias);
                    float sunoccluded = filtershadow(csmtc);
                    @(if (dlopt "m") [result [
                        light += diffuse.rgb*sunfacing * sunlightcolor * sunoccluded;
                    ]] [result [
                        @(if (= (+ $numlights (dlopt "c")) 1) [unpackspec])
                        float sunspec = pow(clamp(sunfacing*facing - dot(camdir, sunlightdir), 0.0, 1.0), gloss) * specscale;
                        @(if (dlopt "r") [result [
                            sunlight += (diffuse.rgb*sunfacing + sunspec) * sunoccluded;
                        ]] [result [
                            @(? (&& (dlopt "A") [! $avatar] [! $transparent]) [
                                sunoccluded *= aoparams.z + aomask*aoparams.w;
                            ])
                            light += (diffuse.rgb*sunfacing + sunspec) * sunoccluded * sunlightcolor;
                        ]])
                    ]])
                }
                @(if (dlopt "r") [result [
                    @(? (&& (dlopt "A") [! $avatar] [! $transparent]) [
                        sunlight *= aoparams.z + aomask*aoparams.w;
                    ])
                    light += sunlight * sunlightcolor;
                ]])
            ]])

            @(loopconcat j $numlights [result [
                vec3 light@[j]dir = lightpos[@@j].xyz - pos.xyz * lightpos[@@j].w;
                float light@[j]dist2 = dot(light@[j]dir, light@[j]dir);
                if(light@[j]dist2 < 1.0)
                {
                    @(if (= (+ $numlights $baselight) 1) [result [
                        vec4 normal = gfetch(tex1, gl_FragCoord.xy);
                        @(? (&& $transparent [! $ghasstencil]) [
                            if(normal.x + normal.y == 0.0) discard;
                        ])
                        normal.xyz = normal.xyz*2.0 - 1.0;
                        @(? $usepacknorm [
                            float glowscale = dot(normal.xyz, normal.xyz);
                            normal.xyz *= inversesqrt(glowscale);
                        ] [
                            #define glowscale normal.a
                        ])
                    ]])
                    float light@[j]facing = dot(light@[j]dir, normal.xyz);
                    if(light@[j]facing > 0.0)
                    {
                        float light@[j]invdist = inversesqrt(light@[j]dist2); 
                        @(if $spotlight [result [
                            float spot@[j]dist = dot(light@[j]dir, spotparams[@@j].xyz);
                            float spot@[j]atten = 1.0 - (1.0 - light@[j]invdist * spot@[j]dist) * spotparams[@@j].w;
                            if(spot@[j]atten > 0.0)
                            {
                        ]])
                        float light@[j]atten = 1.0 - light@[j]dist2 * light@[j]invdist;
                        @(? (&& (= (+ $numlights $baselight) 1) [! (dlopt "m")]) [
                            float fogcoord = length(camera - pos.xyz);
                        ])
                        @(if (= (+ (? (dlopt "p") $numlights) (dlopt "c")) 1) [unpackdistbias])
                        @(if $spotlight [
                            if (dlopt "p") [result [
                                vec3 spot@[j]tc = getspottc(light@[j]dir, spot@[j]dist, spotparams[@@j], shadowparams[@@j], shadowoffset[@@j], distbias * lightpos[@@j].w);
                                light@[j]atten *= spot@[j]atten * filtershadow(spot@[j]tc);
                            ]] [result [
                                light@[j]atten *= spot@[j]atten;
                            ]]
                        ] [
                            if (dlopt "p") [result [
                                vec3 shadow@[j]tc = getshadowtc(light@[j]dir, shadowparams[@@j], shadowoffset[@@j], distbias * lightpos[@@j].w);
                                light@[j]atten *= filtershadow(shadow@[j]tc);
                            ]]
                        ])
                        @(if (= (+ $numlights $baselight) 1) [result [
                            vec4 diffuse = gfetch(tex0, gl_FragCoord.xy);
                            @(if (! $transparent) [result [
                                @(if $usepacknorm [unpacknorm glowscale])
                                diffuse.rgb *= glowscale;
                            ]])
                        ]])
                        light@[j]facing *= light@[j]invdist;
                        @(if (dlopt "m") [result [
                            light += diffuse.rgb*light@[j]facing * lightcolor[@@j].rgb * light@[j]atten;
                        ]] [result [
                            @(if (= (+ $numlights (dlopt "c")) 1) [unpackspec])
                            float light@[j]spec = pow(clamp(light@[j]facing*facing - light@[j]invdist*dot(camdir, light@[j]dir), 0.0, 1.0), gloss) * specscale;
                            @(if (dlopt "z") [result [
                                light@[j]spec *= lightcolor[@@j].a;
                            ]]) 
                            light += (diffuse.rgb*light@[j]facing + light@[j]spec) * lightcolor[@@j].rgb * light@[j]atten;
                            @(? (= (+ $numlights $baselight) 1) [
                                float foglerp = clamp(exp2(fogcoord*fogdensity.x)*fogdensity.y, 0.0, 1.0);
                                light *= foglerp;
                            ])
                        ]])
                        @(? $spotlight [}])
                    }
                }
            ]])
            @(if (dlopt "m") [if $baselight [result [
                accumlight(light);
                accumalpha(alpha);
            ]] [result [
                accumlight(light);
                accumalpha(0.0);
            ]]] [if (|| $baselight [> $numlights 1]) [result [
                float foglerp = clamp(exp2(fogcoord*fogdensity.x)*fogdensity.y, 0.0, 1.0);
                @(? $baselight [
                    accumlight(mix(fogcolor*alpha, light, foglerp));
                    accumalpha(alpha);
                ] [
                    accumlight(light*foglerp);
                    accumalpha(0.0);
                ])
            ]] [result [
                accumlight(light);
                accumalpha(0.0);
            ]]])

            @(if (dlopt "R") [result [
                    @(? (dlopt "T") [if(!shouldresolve) break;])
                }

                @(? (dlopt "T") [if(shouldresolve)]) resolved *= @(divf 1 $msaasamples);
                fragcolor = resolved;
            ]])
        }
    ] $arg7
]

deferredlightshader = [
    deferredlighttype = (concatword $arg1 $arg2 $arg3)
    shadername = (concatword "deferredlight" $deferredlighttype)
    basevariants = (* (max $arg6 1) 2 (? (dlopt "b") 2 1) (? (dlopt "s") 2 1))
    maxvariants = $basevariants
    if (dlopt "t") [maxvariants = (+ $maxvariants $basevariants 1)]
    if (dlopt "d") [maxvariants = (+ $maxvariants $basevariants 1)]
    deferredlightvariantshader $shadername -1 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // base shader, no points lights, sunlight
    if (dlopt "t") [
        deferredlightvariantshader $shadername 16 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // row 16, trasparency, base shader, no points lights, sunlight
    ]
    if (dlopt "d") [
        deferredlightvariantshader $shadername 17 (concatword $arg1 $arg3) $arg4 $arg5 0 $maxvariants // row 17, avatar, base shader, no points lights, sunlight
    ]
    loop+ i 1 (max $arg6 1) [
        if (dlopt "b") [
            deferredlightvariantshader $shadername 0 (concatword $arg1 $arg3) $arg4 $arg5 $i $maxvariants // row 0, point lights, sunlight
            deferredlightvariantshader $shadername 1 (concatword $arg1 $arg2 $arg3) $arg4 $arg5 $i $maxvariants // row 1, shadowed point lights, sunlight
        ]
        deferredlightvariantshader $shadername 2 $arg1 $arg4 $arg5 $i $maxvariants // row 2, point lights 
        deferredlightvariantshader $shadername 3 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 3, shadowed point lights
        if (dlopt "s") [
            if (dlopt "b") [
                deferredlightvariantshader $shadername 4 (concatword $arg1 $arg3) $arg4 $arg5 $i $maxvariants // row 4, spot lights, sunlight
                deferredlightvariantshader $shadername 5 (concatword $arg1 $arg2 $arg3) $arg4 $arg5 $i $maxvariants // row 5, shadowed spot lights, sunlight
            ]
            deferredlightvariantshader $shadername 6 $arg1 $arg4 $arg5 $i $maxvariants // row 6, spot lights 
            deferredlightvariantshader $shadername 7 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 7, shadowed spot lights
        ]
        if (dlopt "t") [
            if (dlopt "b") [
                deferredlightvariantshader $shadername 8 (concatword $arg1 $arg3) $arg4 $arg5 $i $maxvariants // row 8, transparent, point lights, sunlight
                deferredlightvariantshader $shadername 9 (concatword $arg1 $arg2 $arg3) $arg4 $arg5 $i $maxvariants // row 9, transparent, shadowed point lights, sunlight
            ]
            deferredlightvariantshader $shadername 10 $arg1 $arg4 $arg5 $i $maxvariants // row 10, transparent, point lights 
            deferredlightvariantshader $shadername 11 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 11, transparent, shadowed point lights
            if (dlopt "s") [
                if (dlopt "b") [
                    deferredlightvariantshader $shadername 12 (concatword $arg1 $arg3) $arg4 $arg5 $i $maxvariants // row 12, transparent, spot lights, sunlight
                    deferredlightvariantshader $shadername 13 (concatword $arg1 $arg2 $arg3) $arg4 $arg5 $i $maxvariants // row 13, transparent, shadowed spot lights, sunlight
                ]
                deferredlightvariantshader $shadername 14 $arg1 $arg4 $arg5 $i $maxvariants // row 14, transparent, spot lights 
                deferredlightvariantshader $shadername 15 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 15, transparent, shadowed spot lights
            ]
        ]
        if (dlopt "d") [
            if (dlopt "b") [
                deferredlightvariantshader $shadername 24 (concatword $arg1 $arg3) $arg4 $arg5 $i $maxvariants // row 24, avatar, point lights, sunlight
                deferredlightvariantshader $shadername 25 (concatword $arg1 $arg2 $arg3) $arg4 $arg5 $i $maxvariants // row 25, avatar, shadowed point lights, sunlight
            ]
            deferredlightvariantshader $shadername 26 $arg1 $arg4 $arg5 $i $maxvariants // row 26, avatar, point lights 
            deferredlightvariantshader $shadername 27 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 27, avatar, shadowed point lights
            if (dlopt "s") [
                if (dlopt "b") [
                    deferredlightvariantshader $shadername 28 (concatword $arg1 $arg3) $arg4 $arg5 $i $maxvariants // row 28, avatar, spot lights, sunlight
                    deferredlightvariantshader $shadername 29 (concatword $arg1 $arg2 $arg3) $arg4 $arg5 $i $maxvariants // row 29, avatar, shadowed spot lights, sunlight
                ]
                deferredlightvariantshader $shadername 30 $arg1 $arg4 $arg5 $i $maxvariants // row 30, avatar, spot lights 
                deferredlightvariantshader $shadername 31 (concatword $arg1 $arg2) $arg4 $arg5 $i $maxvariants // row 31, avatar, shadowed spot lights
            ]
        ]
    ]
]

