
#define Depth_Write_POM
#define POM_DEPTH 0.1     // [0.025 0.05 0.075 0.1 0.125 0.15 0.20 0.25 0.30 0.50 0.75 1.0]
#define MAX_ITERATIONS 40 // [5 10 15 20 25 30 40 50 60 70 80 90 100 125 150 200 400]
#define MAX_DIST 40.0     // [5.0 10.0 15.0 20.0 25.0 30.0 40.0 50.0 60.0 70.0 80.0 90.0 100.0 125.0 150.0 200.0 400.0]
const float MAX_OCCLUSION_DISTANCE = MAX_DIST;
const float MIX_OCCLUSION_DISTANCE = MAX_DIST * 0.9;
const int MAX_OCCLUSION_POINTS = MAX_ITERATIONS;
// #define USE_LUMINANCE_AS_HEIGHTMAP

const float mincoord = 1.0 / 4096.0;

varying vec4 vtexcoordam; // .st for add, .pq for mul
varying vec4 vtexcoord;

vec2 dcdx = dFdx(vtexcoord.st * vtexcoordam.pq) * exp2(0);
vec2 dcdy = dFdy(vtexcoord.st * vtexcoordam.pq) * exp2(0);

vec4 readNormal(in vec2 coord)
{
    return texture2DGradARB(normals, fract(coord) * vtexcoordam.pq + vtexcoordam.st, dcdx, dcdy);
}
vec4 readTexture(in vec2 coord)
{
    return texture2DGradARB(texture, fract(coord) * vtexcoordam.pq + vtexcoordam.st, dcdx, dcdy);
}
vec4 readTexture3(in vec2 coord)
{
    return texture2DGradARB(texture, fract(coord) * vtexcoordam.pq + vtexcoordam.st, dcdx, dcdy);
}

#ifndef USE_LUMINANCE_AS_HEIGHTMAP
vec2 pomColor(vec2 uv, vec3 fragpos, float noise, mat3 tbn)
{

    vec3 coord = vec3(vtexcoord.st, 1.0);
    vec2 adjustedTexCoord = uv;
    vec3 viewVector = normalize(tbn * fragpos);
    float dist = length(fragpos);
    gl_FragDepth = gl_FragCoord.z;

    if (dist < MAX_OCCLUSION_DISTANCE)
    {

        if (viewVector.z < 0.0 && readNormal(vtexcoord.st).a < 0.9999 && readNormal(vtexcoord.st).a > 0.00001)
        {
            vec3 interval = viewVector.xyz / -viewVector.z / MAX_OCCLUSION_POINTS * POM_DEPTH;
            vec2 atlasAspect = vec2(atlasSize.y / float(atlasSize.x), atlasSize.x / float(atlasSize.y));
            vec2 viewCorrection = max(vec2((vtexcoordam.q) / (vtexcoordam.p) * atlasAspect.x, 1.0),
                                      vec2(1.0, (vtexcoordam.p) / (vtexcoordam.q) * atlasAspect.y));
            interval.xy *= viewCorrection;

            coord += noise * interval;
            float sumVec = 1;
            float numLayers = mix(MAX_OCCLUSION_POINTS, 16, max(dot(vec3(0.0, 0.0, 1.0), fragpos), 0.0));

            for (int loopCount = 0; (loopCount < numLayers) &&
                                    (1.0 - POM_DEPTH + POM_DEPTH * readNormal(coord.st).a < coord.p) && coord.p >= 0.0;
                 ++loopCount)
            {
                coord = coord + interval;
                sumVec += 1.0;
            }

            if (coord.t < mincoord)
            {
                if (readTexture(vec2(coord.s, mincoord)).a == 0.0)
                {

                    coord.t = mincoord;

                    discard;
                }
            }

            adjustedTexCoord =
                mix(fract(coord.st) * vtexcoordam.pq + vtexcoordam.st, adjustedTexCoord,
                    max(dist - MIX_OCCLUSION_DISTANCE, 0.0) / (MAX_OCCLUSION_DISTANCE - MIX_OCCLUSION_DISTANCE));

            vec3 truePos = fragpos + sumVec * inverse(tbn) * interval;
            gl_FragDepth = toClipSpace3(truePos).z;
        }
    }
    return adjustedTexCoord;
}
#else
vec2 pomColor(vec2 uv, vec3 fragpos, float noise, mat3 tbn)
{

    vec3 coord = vec3(vtexcoord.st, 1.0);
    vec2 adjustedTexCoord = uv;
    vec3 viewVector = normalize(tbn * fragpos);
    float dist = length(fragpos);
    gl_FragDepth = gl_FragCoord.z;

    if (dist < MAX_OCCLUSION_DISTANCE)
    {

        if (viewVector.z < 0.0)

        {
            float pomd = POM_DEPTH;
            vec3 interval = viewVector.xyz / -viewVector.z / MAX_OCCLUSION_POINTS * pomd;
            vec2 atlasAspect = vec2(atlasSize.y / float(atlasSize.x), atlasSize.x / float(atlasSize.y));
            vec2 viewCorrection = max(vec2((vtexcoordam.q) / (vtexcoordam.p) * atlasAspect.x, 1.0),
                                      vec2(1.0, (vtexcoordam.p) / (vtexcoordam.q) * atlasAspect.y));
            interval.xy *= viewCorrection;

            coord += noise * interval;
            float sumVec = 1;

            // float lum0 = luma((texture(texture, midtex).rgb)) * 0.75;
            float lum0 = luma(texture2DLod(texture, lmtexcoord.xy, 100).rgb);

            float numLayers = mix(MAX_OCCLUSION_POINTS, 16, max(dot(vec3(0.0, 0.0, 1.0), fragpos), 0.0));

            for (int loopCount = 0; (loopCount < numLayers) &&
                                    (1.0 - pomd + pomd * luma((readTexture(coord.st).rgb)) / lum0 * 0.5 < coord.p) && coord.p >= 0.0;
                 ++loopCount)
            {
                coord = coord + interval;
                sumVec += 1.0;
            }

            if (coord.t < mincoord)
            {
                if (readTexture(vec2(coord.s, mincoord)).a == 0.0)
                {

                    coord.t = mincoord;

                    discard;
                }
            }

            adjustedTexCoord =
                mix(fract(coord.st) * vtexcoordam.pq + vtexcoordam.st, adjustedTexCoord,
                    max(dist - MIX_OCCLUSION_DISTANCE, 0.0) / (MAX_OCCLUSION_DISTANCE - MIX_OCCLUSION_DISTANCE));

            vec3 truePos = fragpos + sumVec * inverse(tbn) * interval;
            gl_FragDepth = toClipSpace3(truePos).z;
        }
    }
    return adjustedTexCoord;
}

#endif