#shader vertex
#version 440 core

layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec2 UVPosition;

out flat vec2 UV;
out mat4 InvView;
out mat4 InvProjection;

uniform mat4 u_View;
uniform mat4 u_Projection;

void main()
{
    UV = UVPosition;
    InvView = inverse(u_View);
    InvProjection = inverse(u_Projection);
    gl_Position = vec4(VertexPosition, 1.0f);
}

#shader fragment
#version 440 core

float ditherPattern[4][4] = { { 0.0f, 0.5f, 0.125f, 0.625f},
{ 0.75f, 0.22f, 0.875f, 0.375f},
{ 0.1875f, 0.6875f, 0.0625f, 0.5625},
{ 0.9375f, 0.4375f, 0.8125f, 0.3125} };

in mat4 InvView;
in mat4 InvProjection;

uniform sampler2D u_Depth;

uniform vec3 u_CamPosition;
uniform int u_StepCount;
uniform float u_FogAmount;
uniform float u_Exponant;

const int MAX_LIGHT = 20;
uniform int u_LightCount;

struct Light {
    mat4 transform;
    vec3 color;
    vec3 direction;
    sampler2D shadowmap;
    float strength;
};

uniform Light u_Lights[MAX_LIGHT];

uniform sampler2D lightShadowmap;
in vec2 UV;

out vec4 FragColor;
const float PI = 3.141592653589793f;

// Mie scaterring approximated with Henyey-Greenstein phase function.
float ComputeScattering(float lightDotView)
{
    float result = 1.0f - u_FogAmount ;
    result /= (4.0f * PI * pow(1.0f + u_FogAmount * u_FogAmount - (1.0f * u_FogAmount) * lightDotView, 1.5f));
    return result;
}

vec3 ComputeVolumetric(vec3 FragPos, Light light)
{
    vec3 startPosition = u_CamPosition;             // Camera Position
    vec3 rayVector = FragPos - startPosition;  // Ray Direction

    float rayLength = length(rayVector);            // Length of the raymarched
    if(rayLength > 1000.0)
        return vec3(0.0);
    float stepLength = rayLength / u_StepCount;        // Step length
    vec3 rayDirection = rayVector / rayLength;
    vec3 step = rayDirection * stepLength;          // Normalized to step length direction

    vec3 accumFog = vec3(0.0f, 0.0f, 0.0f);         // accumulative color

    // Raymarching
    vec3 currentPosition = startPosition;
    for (int i = 0; i < u_StepCount; i++)
    {
        vec4 fragPosLightSpace = light.transform * vec4(currentPosition, 1.0f);
        // perform perspective divide
        vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;

        // transform to [0,1] range
        projCoords = projCoords * 0.5 + 0.5;

        float currentDepth = projCoords.z;
        float closestDepth = texture(light.shadowmap, projCoords.xy).r;
        if (closestDepth > currentDepth && closestDepth < 999)
        {
            //accumFog = vec3(light.color);
            accumFog += (ComputeScattering(dot(rayDirection, light.direction)).xxx * light.color);
            //accumFog = vec3(projCoords.x, projCoords.y, 1.0);
            
        }
        currentPosition += step * ditherPattern[int(gl_FragCoord.x) % 4][int(gl_FragCoord.y) % 4];
        //accumFog = vec3(projCoords);
    }
    accumFog /= u_StepCount;

    return accumFog;
}

// Converts depth to World space coords.
vec3 WorldPosFromDepth(float depth) {
    float z = depth * 2.0 - 1.0;

    vec4 clipSpacePosition = vec4(UV * 2.0 - 1.0, z, 1.0);
    vec4 viewSpacePosition = InvProjection * clipSpacePosition;

    // Perspective division
    viewSpacePosition /= viewSpacePosition.w;

    vec4 worldSpacePosition = InvView * viewSpacePosition;

    return worldSpacePosition.xyz;
}

void main()
{
    float depth = texture(u_Depth, UV).r;
    vec3 globalFragmentPosition = WorldPosFromDepth(depth);

    vec3 fog = vec3(0, 0, 0);
    for (int i = 0; i < u_LightCount; i++)
    {
        fog += ComputeVolumetric(globalFragmentPosition, u_Lights[i]) * u_Exponant;
    }

    FragColor = vec4(fog, 1.0);
    //FragColor = vec4(mix(fog, ComputeVolumetric(globalFragmentPosition, u_Lights[0]), 0.9f), 0.01);
    //FragColor = vec4(globalFragmentPosition * 10.0, 1.0f);
    //FragColor = vec4(globalFragmentPosition.xyz * 100.0, 1.0f);
    //FragColor = vec4(worldSpacePosition.x, worldSpacePosition.y, worldSpacePosition.z, 1);
    //FragColor = vec4(ComputeVolumetric(globalFragmentPosition, u_Lights[0]), 1.0);
}