#version 330 core

uniform sampler2D depth_tex;

uniform vec2 fg_NearFar;
uniform mat4 fg_ViewMatrixInverse;
uniform mat4 fg_ProjectionMatrixInverse;

/*
 * Reconstruct the view space position from the depth buffer. Mostly used by
 * fullscreen post-processing shaders.
 *
 * Given a 2D screen UV in the range [0,1] and a depth value from the depth
 * buffer, also in the [0,1] range, return the view space position.
 */
vec3 get_view_space_from_depth(vec2 uv, float depth, mat4 proj_matrix_inverse)
{
    /*
     * We are using a reversed depth buffer. 1.0 corresponds to the near plane
     * and 0.0 to the far plane. We convert this back to NDC space by doing
     * 1.0 - depth          to undo the depth reversal
     * 2.0 * depth - 1.0    to transform it to NDC space [-1,1]
     */
    vec4 ndc_p = vec4(uv * 2.0 - 1.0, 1.0 - depth * 2.0, 1.0);
    vec4 vs_p = proj_matrix_inverse * ndc_p;
    return vs_p.xyz / vs_p.w;
}

vec3 get_view_space_from_depth(vec2 uv, float depth)
{
    return get_view_space_from_depth(uv, depth, fg_ProjectionMatrixInverse);
}

vec3 get_view_space_from_depth(vec2 uv)
{
    return get_view_space_from_depth(uv, texture(depth_tex, uv).r);
}

vec3 get_world_space_from_depth(vec2 uv, float depth)
{
    vec4 vs_p = vec4(get_view_space_from_depth(uv, depth), 1.0);
    return (fg_ViewMatrixInverse * vs_p).xyz;
}

vec3 get_world_space_from_depth(vec2 uv)
{
    return get_world_space_from_depth(uv, texture(depth_tex, uv).r);
}

float linearize_depth(float depth)
{
    // Undo the depth reversal
    float z = 1.0 - depth;
    return 2.0 * fg_NearFar.x
        / (fg_NearFar.y + fg_NearFar.x - z * (fg_NearFar.y - fg_NearFar.x));
}