vec3 project_and_divide(mat4 Projection_mat, vec3 x) {
    vec4 HomogeneousPos = Projection_mat * vec4(x, 1);
    return HomogeneousPos.xyz / HomogeneousPos.w;
}

vec3 screen_view(vec3 x, bool IsDH, const bool ShouldUnjitter) {
    x = x * 2 - 1;

    if(ShouldUnjitter)
    x.xy -= taaJitter;

    mat4 ProjMat = IsDH ? dhProjectionInverse : gbufferProjectionInverse;
    return project_and_divide(ProjMat, x);
}

vec3 view_player(vec3 x) {
    return mat3(gbufferModelViewInverse) * x;
}

vec3 player_view(vec3 x) {
    return mat3(gbufferModelView) * x;
}

vec3 view_screen(vec3 x, bool IsDH, const bool ShouldJitter) {
    mat4 ProjMat = IsDH ? dhProjection : gbufferProjection;
    x = project_and_divide(ProjMat, x);

    if(ShouldJitter)
        x.xy += taaJitter;

    x = x * 0.5 + 0.5;
    return x;
}

struct Positions {
    vec3 Screen, View, Player, ViewN, PlayerN, World;
};

Positions get_positions(vec2 texcoord, float Depth, bool IsDH, const bool ShouldJitter) {
    Positions Pos;
    Pos.Screen = vec3(texcoord, Depth);
    Pos.View = screen_view(Pos.Screen, IsDH, ShouldJitter);
    Pos.Player = view_player(Pos.View);
    Pos.ViewN = normalize(Pos.View);
    Pos.PlayerN = normalize(Pos.Player);
    Pos.World = Pos.Player + cameraPosition;
    return Pos;
}

vec3 player_shadow(vec3 PlayerPos) {
    vec3 ShadowPos = project_and_divide(shadowProjection, (shadowModelView * vec4(PlayerPos + gbufferModelViewInverse[3].xyz, 1)).xyz); //convert to shadow ndc space
    return ShadowPos;
}

vec2 toPrevScreenPos(vec2 currScreenPos, float depth, bool isDH) {
    mat4 ProjInv = isDH ? dhProjectionInverse : gbufferProjectionInverse;
    mat4 ModelViewInv = gbufferModelViewInverse;
    mat4 PrevModelView = gbufferPreviousModelView;
    mat4 PrevProj = isDH ? dhPreviousProjection : gbufferPreviousProjection;
    vec3 currViewPos = vec3(vec2(ProjInv[0].x, ProjInv[1].y) * (currScreenPos.xy * 2.0 - 1.0) + ProjInv[3].xy, ProjInv[3].z);
    currViewPos /= (ProjInv[2].w * (depth * 2.0 - 1.0) + ProjInv[3].w);
    vec3 currFeetPlayerPos = mat3(ModelViewInv) * currViewPos + ModelViewInv[3].xyz;

    vec3 prevFeetPlayerPos = depth > 0.56 ? currFeetPlayerPos + cameraPosition - previousCameraPosition : currFeetPlayerPos;
    vec3 prevViewPos = mat3(PrevModelView) * prevFeetPlayerPos + PrevModelView[3].xyz;
    vec2 finalPos = vec2(PrevProj[0].x, PrevProj[1].y) * prevViewPos.xy + PrevProj[3].xy;
    return (finalPos / -prevViewPos.z) * 0.5 + 0.5;
}

#define linear_srgb(linear) ( mix(12.92 * linear, 1.055 * pow(linear, vec3(1/2.4)) - 0.055, step(0.0031308, linear)) )

#define srgb_linear(srgb) ( mix(srgb / 12.92, pow(((srgb + 0.055)/(1.055)), vec3(2.4)), step(0.04045, srgb)))
