////////////////////////////////////////////////
//
// ambient obscurance
//
////////////////////////////////////////////////

aotapoffsets = [
"-0.933103, 0.025116"
"-0.432784, -0.989868"
"0.432416, -0.413800"
"-0.117770, 0.970336"
"0.837276, 0.531114"
"-0.184912, 0.200232"
"-0.955748, 0.815118"
"0.946166, -0.998596"
"-0.897519, -0.581102"
"0.979248, -0.046602"
"-0.155736, -0.488204"
"0.460310, 0.982178"
]

ambientobscurancevariantshader = [
    lineardepth = (>= (strstr $arg2 "l") 0)
    packeddepth = (>= (strstr $arg2 "p") 0)
    derivnormal = (>= (strstr $arg2 "d") 0)
    maxaotaps = $arg3 
    shader 0 $arg1 [
        in vec4 vvertex;
        @(screentexcoord 0)
        @(screentexcoord 1)
        out vec2 texcoord0, texcoord1;
        void main(void)
        {
            gl_Position = vvertex;
            texcoord0 = vtexcoord0; 
            texcoord1 = vtexcoord1;
        }
    ] [
        @(gfetchdefs tex0 (&& $msaasamples [! $lineardepth]) gdepthfetch)
        @(gfetchdefs tex1 $msaasamples gnormfetch)
        @(gdepthunpackparams)
        uniform sampler2D tex2;
        uniform vec3 tapparams;
        uniform vec2 contrastparams;
        uniform vec4 offsetscale;
        uniform float prefilterdepth;
        @(? (! $derivnormal) [
            uniform mat3 normalmatrix;
        ])
        @(? $lineardepth [
            #define depthtc gl_FragCoord.xy
        ] [
            #define depthtc texcoord0
        ])
        uniform vec3 gdepthpackparams;
        in vec2 texcoord0, texcoord1;
        layout(location = 0) out vec4 fragcolor;
        void main(void)
        {
            @(if (&& $derivnormal [= $aodepthformat 1]) [result [
                @(gdepthunpack depth [gnormfetch(tex1, texcoord0)] [
                    vec2 tapscale = tapparams.xy/depth;
                ] [
                    float w = depth*gdepthscale.y + gdepthscale.z;
                    depth = gdepthscale.x/w;
                    vec2 tapscale = tapparams.xy*w;
                ])
            ]] [result [
                @(gdepthunpack depth [gdepthfetch(tex0, depthtc)] [
                    vec2 tapscale = tapparams.xy/depth;
                ] [
                    float w = depth*gdepthscale.y + gdepthscale.z;
                    depth = gdepthscale.x/w;
                    vec2 tapscale = tapparams.xy*w;
                ] $lineardepth (? $lineardepth (! $aodepthformat) (= $gdepthformat 1)) packdepth)
            ]])
            vec2 dpos = depthtc*offsetscale.xy + offsetscale.zw, pos = depth*dpos;
            @(if $derivnormal [result [
                vec2 ddepth = vec2(dFdx(depth), dFdy(depth)); 
                ddepth *= step(abs(ddepth), vec2(4.0));
                vec3 normal;
                normal.xy = (depth+ddepth.yx)*offsetscale.yx;
                normal.z = normal.x*normal.y;
                normal.xy *= -ddepth;
                normal.z -= dot(dpos, normal.xy);
                normal = normalize(normal);
            ]] [result [
                vec3 normal = gnormfetch(tex1, texcoord0).rgb*2.0 - 1.0;
                float normscale = inversesqrt(dot(normal, normal));
                normal *= normscale > 0.75 ? normscale : 0.0;
                normal = normalmatrix * normal;
            ]])
            vec2 noise = texture(tex2, texcoord1).rg*2.0-1.0;
            float obscure = 0.0;
            @(loopconcat i $maxaotaps [result [
                vec2 offset@[i] = reflect(vec2(@(at $aotapoffsets $i)), noise);
                offset@[i] = depthtc + tapscale * offset@[i];
                @(gdepthunpack [depth@[i]] [gdepthfetch(tex0, offset@[i])] [] [] $lineardepth (&& $lineardepth (! $aodepthformat)))
                vec3 v@[i] = vec3(depth@[i]*(offset@[i]*offsetscale.xy + offsetscale.zw) - pos, depth@[i] - depth);
                float dist2@[i] = dot(v@[i], v@[i]);
                obscure += step(dist2@[i], tapparams.z) * max(0.0, dot(v@[i], normal) + depth*1.0e-2) / (dist2@[i] + 1.0e-5);
            ]])
            obscure = pow(clamp(1.0 - contrastparams.x*obscure, 0.0, 1.0), contrastparams.y);
            @(if $derivnormal [result [
                vec2 weights = step(abs(ddepth), vec2(prefilterdepth)) * (2.0*fract((gl_FragCoord.xy - 0.5)*0.5) - 0.5);
            ]] [result [
                vec2 weights = step(fwidth(depth), prefilterdepth) * (2.0*fract((gl_FragCoord.xy - 0.5)*0.5) - 0.5);
            ]])
            obscure -= dFdx(obscure) * weights.x;
            obscure -= dFdy(obscure) * weights.y;
            @(if $packeddepth [
                if $aodepthformat [result [
                    fragcolor.rg = vec2(obscure, depth);
                ]] [result [
                    @(if (&& (! $lineardepth) (!= $gdepthformat 1)) [gpackdepth packdepth depth])
                    fragcolor = vec4(packdepth, obscure);
                ]]
            ] [result [
                fragcolor = vec4(obscure, 0.0, 0.0, 1.0);
            ]])
        }
    ]
]

ambientobscuranceshader = [
    ambientobscurancevariantshader (format "ambientobscurance%1%2" $arg1 $arg2) $arg1 $arg2
]

shader 0 "linearizedepth" [
    in vec4 vvertex;
    @(screentexcoord 0)
    out vec2 texcoord0;
    void main(void)
    {
        gl_Position = vvertex;
        texcoord0 = vtexcoord0; 
    }
] [
    @(gfetchdefs tex0 $msaasamples)
    uniform vec3 gdepthpackparams;
    in vec2 texcoord0;
    layout(location = 0) out vec4 fragcolor;
    void main(void)
    {
        @(if (! $aodepthformat) [
            if (= $gdepthformat 1) [result [
                fragcolor = gfetch(tex0, texcoord0);
            ]] [result [
                @(gdepthunpack depth [gfetch(tex0, texcoord0)])
                @(gpackdepth packdepth depth)
                fragcolor = vec4(packdepth, 1.0);
            ]]
        ] [result [
            @(gdepthunpack depth [gfetch(tex0, texcoord0)])
            fragcolor.r = depth;
        ]])
    }
]

bilateralvariantshader = [
    linear = (>= (strstr $arg2 "l") 0)
    packed = (>= (strstr $arg2 "p") 0)
    upscaled = (>= (strstr $arg2 "u") 0)
    numtaps = $arg3
    reduced = $arg4
    filterdir = $arg5
    shader 0 $arg1 [
        in vec4 vvertex;
        @(if $reduced [result [
            @(screentexcoord 0)
            out vec2 texcoord0;
        ]])
        @(if $upscaled [result [
            @(screentexcoord 1)
            out vec2 texcoord1;
        ]])
        void main(void)
        {
            gl_Position = vvertex;
            @(? $reduced [texcoord0 = vtexcoord0;])
            @(? $upscaled [texcoord1 = vtexcoord1;])
        }
    ] [
        @(gfetchdefs tex1 (&& $msaasamples [! $linear]))
        uniform sampler2DRect tex0;
        uniform vec2 bilateralparams;
        uniform vec3 gdepthpackparams;
        @(? $reduced [in vec2 texcoord0;])
        @(? $upscaled [in vec2 texcoord1;])
        layout(location = 0) out vec4 fragcolor;
        void main(void)
        {
            #define tc @(? $upscaled [texcoord1] [gl_FragCoord.xy])
            #define depthtc @(? $reduced [texcoord0] [gl_FragCoord.xy])
            #define tapvec(type, i) @(? (=s $filterdir "x") [type(i, 0.0)] [type(0.0, i)])
            #define texval(i) textureRect(tex0, tc + tapvec(vec2, i))
            #define texvaloffset(i) textureRectOffset(tex0, tc, tapvec(ivec2, i))
            #define depthval(i) gfetch(tex1, depthtc + tapvec(vec2, i))
            #define depthvaloffset(i) gfetchoffset(tex1, depthtc, tapvec(ivec2, i))
            @(cond [$packed] [
                if $aodepthformat [result [
                    vec2 vals = textureRect(tex0, tc).rg;
                    #define color vals.x
                    @(if $upscaled [gdepthunpack depth [gfetch(tex1, depthtc)]] [result [
                        #define depth vals.y
                    ]])
                ]] [result [
                    vec4 vals = textureRect(tex0, tc);
                    #define color vals.a
                    @(if $upscaled [gdepthunpack depth [gfetch(tex1, depthtc)]] [result [
                        float depth = dot(vals.rgb, gdepthunpackparams);
                    ]])
                ]]
            ] [$linear] [result [
                float color = gfetch(tex0, tc).r;
                @(if $aodepthformat [result [
                    float depth = gfetch(tex1, depthtc).r;
                ]] [result [
                    float depth = dot(gfetch(tex1, depthtc).rgb, gdepthunpackparams);
                ]])
            ]] [result [
                float color = textureRect(tex0, tc).r;
                @(gdepthunpack depth [gfetch(tex1, depthtc)])
            ]])
            float weights = 1.0;
            @(loopconcat i (* 2 $numtaps) [
                curtap = (- $i $numtaps)
                if (>= $curtap 0) [curtap = (+ $curtap 1)]
                curtapoffset = (* $curtap 2)
                curdepthoffset = (<< $curtapoffset $reduced)
                curtexval = [texval@(? (<= $mintexrectoffset $curtapoffset $maxtexrectoffset) "offset")(@(+f $curtapoffset))]
                curdepthval = [depthval@(? (<= $mintexrectoffset $curdepthoffset $maxtexrectoffset) "offset")(@(+f $curdepthoffset))]
                result [
                    @(cond [$packed] [
                        if $aodepthformat [result [
                            vec2 vals@[i] = @[curtexval].rg;
                            #define color@[i] vals@[i].x
                            #define depth@[i] vals@[i].y
                        ]] [result [
                            vec4 vals@[i] = @[curtexval];
                            #define color@[i] vals@[i].a
                            float depth@[i] = dot(vals@[i].rgb, gdepthunpackparams);                 
                        ]]
                    ] [$linear] [
                        result [
                            float color@[i] = @[curtexval].r;
                            @(if $aodepthformat [result [
                                float depth@[i] = @[curdepthval].r;
                            ]] [result [
                                float depth@[i] = dot(@[curdepthval].rgb, gdepthunpackparams);
                            ]])
                        ]
                    ] [
                        result [
                            float color@[i] = @[curtexval].r;
                            @(gdepthunpack [depth@[i]] $curdepthval)
                        ]
                    ])
                    depth@[i] -= depth;
                    float weight@[i] = exp2(@(-f 0 (* $curtap $curtap))*bilateralparams.x - depth@[i]*depth@[i]*bilateralparams.y); 
                    weights += weight@[i];
                    color += weight@[i] * color@[i];
                ]
            ])
            @(if (&& (=s $filterdir "x") $packed) [
                if $aodepthformat [result [
                    fragcolor.rg = vec2(color / weights, depth);
                ]] [result [
                    @(if $upscaled [gpackdepth packdepth depth] [result [
                        #define packdepth vals.rgb
                    ]])
                    fragcolor = vec4(packdepth, color / weights);
                ]]
            ] [result [
                fragcolor = vec4(color / weights, 0.0, 0.0, 1.0);
            ]])
        }
    ]
]

bilateralshader = [
    bilateralvariantshader (format "bilateralx%1%2" $arg1 $arg2) $arg1 $arg2 $arg3 x
    bilateralvariantshader (format "bilateraly%1%2" $arg1 $arg2) $arg1 $arg2 $arg3 y
]

