uniform mat4 gbufferProjection;
uniform mat4 gbufferProjectionInverse;
uniform mat4 gbufferPreviousProjection;
uniform mat4 gbufferPreviousModelView;
uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferModelView;

uniform vec3 cameraPosition;
uniform vec3 previousCameraPosition;

#define diagonal3(m) vec3((m)[0].x, (m)[1].y, m[2].z)
#define projMAD(m, v) (diagonal3(m) * (v) + (m)[3].xyz)

vec3 toClipSpace3(vec3 viewSpacePosition) {
    return projMAD(gbufferProjection, viewSpacePosition) / -viewSpacePosition.z * 0.5 + 0.5;
}

vec3 toScreenSpace(vec3 p) {
	vec4 iProjDiag = vec4(gbufferProjectionInverse[0].x, gbufferProjectionInverse[1].y, gbufferProjectionInverse[2].zw);
    vec3 p3 = p * 2. - 1.;
    vec4 fragposition = iProjDiag * p3.xyzz + gbufferProjectionInverse[3];
    return fragposition.xyz / fragposition.w;
}

vec3 toScreenSpaceVector(vec3 p) {
    vec4 iProjDiag = vec4(gbufferProjectionInverse[0].x, gbufferProjectionInverse[1].y, gbufferProjectionInverse[2].zw);
    vec3 p3 = p * 2. - 1.;
    vec4 fragposition = iProjDiag * p3.xyzz + gbufferProjectionInverse[3];
    return normalize(fragposition.xyz);
}

vec3 toWorldSpace(vec3 p3) {
    p3 = mat3(gbufferModelViewInverse) * p3 + gbufferModelViewInverse[3].xyz;
    return p3;
}

vec3 toWorldSpaceCamera(vec3 p3) {
    p3 = mat3(gbufferModelViewInverse) * p3 + gbufferModelViewInverse[3].xyz;
    return p3 + cameraPosition;
}

vec3 worldToView(vec3 worldPos) {
    return (gbufferModelView * vec4(worldPos, 0.0)).xyz;
}

vec3 viewToWorld(vec3 viewPos) {
    return (gbufferModelViewInverse * vec4(viewPos, 0.0)).xyz;
}

#define viewMAD(m, v) (mat3(m) * (v) + (m)[3].xyz)

vec3 reproject(vec3 sceneSpace, bool hand) {
    vec3 prevScreenPos = hand ? vec3(0.0) : cameraPosition - previousCameraPosition;
    prevScreenPos = sceneSpace + prevScreenPos;
    prevScreenPos = viewMAD(gbufferPreviousModelView, prevScreenPos);
    prevScreenPos = viewMAD(gbufferPreviousProjection, prevScreenPos) * (0.5 / -prevScreenPos.z) + 0.5;

    return prevScreenPos;
}
vec3 reproject2(vec3 sceneSpace, bool hand) {
    vec3 prevScreenPos = vec3(0.0);
    prevScreenPos = sceneSpace;
    prevScreenPos = viewMAD(gbufferPreviousModelView, prevScreenPos);
    prevScreenPos = viewMAD(gbufferPreviousProjection, prevScreenPos) * (0.5 / -prevScreenPos.z) + 0.5;

    return prevScreenPos;
}
vec3 toClipSpace3Prev(vec3 viewSpacePosition) {
    return projMAD(gbufferPreviousProjection, viewSpacePosition) / -viewSpacePosition.z * 0.5 + 0.5;
}

vec3 ViewSpaceFromScreenSpace(vec3 pos, mat4 inverseProjection) {
    pos = pos * 2.0 - 1.0;

    vec3 viewPosition = vec3(vec2(inverseProjection[0].x, inverseProjection[1].y) * pos.xy + inverseProjection[3].xy, inverseProjection[3].z);
    viewPosition /= inverseProjection[2].w * pos.z + inverseProjection[3].w;

    return viewPosition;
}

vec3 ScreenSpaceFromViewSpace(vec3 viewPosition, mat4 projection) {
    vec3 screenPosition = vec3(projection[0].x, projection[1].y, projection[2].z) * viewPosition + projection[3].xyz;
    return screenPosition * (0.5 / -viewPosition.z) + 0.5;
}

float ViewSpaceFromScreenSpace(float positionScreen, mat4 projectionInverse) {
    return projectionInverse[3].z / (projectionInverse[2].w * (positionScreen * 2.0 - 1.0) + projectionInverse[3].w);
}