#version 330 core
const float Nsamples = 64.0;

// inputz
in vec2 UV;
in vec2 ray;
uniform sampler2D colors;
uniform sampler2D normals;
uniform sampler2D dbuffer;
uniform mat4 projection;
uniform float rad;

// outputz
out vec4 color;

mat2 rotate(float a) {
  return mat2(cos(a), -sin(a), sin(a), cos(a));
}

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

vec3 getPos(vec2 uv) {
  float z = 2.0 * texture(dbuffer, uv).x - 1.0;
  vec4 clipSpace = vec4(2.0*UV-1.0, z, 1.0);
  vec4 homogLoc = inverse(projection) * clipSpace;
  return homogLoc.xyz / homogLoc.w;
}

vec3 rndsample(vec3 o, vec3 n, float i) {
    float p = 3.1415 * rand(UV + o.yz + i);
    float f = 2.0 * 3.1415 * rand(UV + o.xy + i);
    float R = rad * rand(UV + o.zx + vec2(i, o.y));
    vec3 P = o + R * vec3(sin(p) * cos(f), sin(p) * sin(f), cos(p));
    return (-2.0 * float(dot(P - o, n) < 0.0) * dot(P - o, n) / pow(length(P - o), 2.0)) * n + P;
}

float ssao() {
    float ao = 0.0;
    float origz = texture(dbuffer, UV).x;
    vec3 origin = getPos(UV);
    vec3 normal = normalize(texture(normals, UV).xyz);

    for (float i = 0.0; i < Nsamples; i += 1.0) {
        vec3 candidate = rndsample(origin, normal, i);
        vec4 projd = projection * vec4(candidate, 1.0);
        vec2 ss = projd.xy / projd.z;
        vec2 sn = ss*0.5 + vec2(0.5);
        vec4 sz = texture(dbuffer, sn);
        ao += float(length(origin - getPos(sn)) <= rad) * float(projd.z / projd.w <= sz.x); // Range check * contribution
    }
    return ao / Nsamples;
}

void main() {
    color = vec4(vec3(ssao()), 1.0);
}
