uniform vec2 iResolution;
uniform float iTime;
out vec4 fragColor;

#define EPS 0.005
#define MAX_DIST 100.0
#define MAX_ITERATIONS 400

float displacement(vec3 p, float k)
{
	return sin(k*p.x)*sin(k*p.y)*sin(k*p.z);
}

float rand(vec2 st)
{
    return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

float rand(float x)
{
    return fract(sin(dot(x, 12.9898)) * 43758.5453123);
}

float noise(vec2 uv, float intensity, float n)
{
    pR(uv, n + 1.);
    return min(1., (1. / (rand(uv.x * 20. + 1.) + rand(uv.y * 40.))) * intensity);
}

// noise: thanks to morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float hash(float n) { return fract(sin(n) * 1e4); }
float hash(vec2 p) { return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x)))); }
float noise(vec3 x) {
    const vec3 step = vec3(110, 241, 171);
    vec3 i = floor(x);
    vec3 f = fract(x);
    float n = dot(i, step);
    vec3 u = f * f * (3.0 - 2.0 * f);
    return mix(mix(mix( hash(n + dot(step, vec3(0, 0, 0))), hash(n + dot(step, vec3(1, 0, 0))), u.x),
                   mix( hash(n + dot(step, vec3(0, 1, 0))), hash(n + dot(step, vec3(1, 1, 0))), u.x), u.y),
               mix(mix( hash(n + dot(step, vec3(0, 0, 1))), hash(n + dot(step, vec3(1, 0, 1))), u.x),
                   mix( hash(n + dot(step, vec3(0, 1, 1))), hash(n + dot(step, vec3(1, 1, 1))), u.x), u.y), u.z);
}
float noise(vec2 x) {
    vec2 i = floor(x);
    vec2 f = fract(x);
	float a = hash(i);
    float b = hash(i + vec2(1.0, 0.0));
    float c = hash(i + vec2(0.0, 1.0));
    float d = hash(i + vec2(1.0, 1.0));
    vec2 u = f * f * (3.0 - 2.0 * f);
	return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

// returns a polygon distance field with x sides
float poly(vec2 uv, float sides)
{
    float mirrayOriginr = (step(0., uv.x) * 2.) - 1.;

    float a = atan(uv.x, uv.y) + PI * mirrayOriginr;
    float r = TAU / sides;
    return cos(floor(.5 + a / r) * r - a) * length(uv);
}

void camR(inout vec3 p) {
	pR(p.xz, iTime * 0.2);
}

// http://www.iquilezles.org/www/articles/spheredensity/spheredensity.htm
float computeFog(vec3 rayOrigin, vec3 rayDirection, vec3 sphereCenter, float sphereRadius, float dbuffer)
{
    // camR(sphereCenter.zyx);

    float ndbuffer = dbuffer / sphereRadius;
    vec3 rc = (rayOrigin - sphereCenter) / sphereRadius;
	
    float b = dot(rayDirection,rc);
    float c = dot(rc,rc) - 1.;
    float h = b * b - c;

    if(h<0.) return 0.;
	
    h = sqrt(h);
    float t1 = -b - h;
    float t2 = -b + h;

    if( t2 < 0.0f || t1 > ndbuffer ) return 0.;

    t1 = max(t1, 0.);
    t2 = min(t2, ndbuffer);

    float i1 = -(c*t1 + b*t1*t1 + t1*t1*t1/3.);
    float i2 = -(c*t2 + b*t2*t2 + t2*t2*t2/3.);
    return (i2 - i1) * (3./4.);
}

vec4 scene(vec3 p)
{
    vec3 c = vec3(0.);
    float r = 1. / 0.;;

    float panic = 1.;
    
    pR(p.zy, -p.z * 0.001);

    // spin
    // pR(p.xy, (-p.z * (iTime - 4.)) * 0.0008);
    
    // lookaround
    float ln = iTime * 0.7;
    float la = ((sin(((ln + 2.25) / 1.22 - 0.81) / 0.44 )) * (sin(((ln + 2.25) / 1.22 - -3.27) / 3.25 )) * (sin(((ln + 2.25) / 1.22 - 11.7) / 2. ) + 0.46) * (sin(((ln + 2.25) / 1.22 + 25.27) / 15.73 ) * 1.17));
    pR(p.zx, sin(ln) * la * 0.7);

    // look up at end
    pR(p.zy, -max(0., iTime - 27.4) * 2.);

    // look up snake3
    // pR(p.zy, -(1. - min(1., abs(iTime - 22.))) * 0.5);

    // movement
    p.z -= iTime * 3.;
    float size = 10. + iTime * 0.2;
    // p.x += size / 2.;

    // head bobbing!!
    p.y += sin(panic * iTime * 5.2 + 1.4) * 0.05;
    p.z += sin(panic * iTime * 5.2) * 0.05;
    p.y += sin(panic * iTime * 2. + 1.) * 0.05;
    p.z += sin(panic * iTime * 2.) * 0.1;
    

    // golvet faller!!?

    vec3 roofp = p;
    float ground_noise = noise(p.xz * 0.8 + vec2(0.6, 0.2) * -iTime * 10.) * iTime * 0.01;

    vec3 box2p = p;
    vec3 box3p = p;
    vec3 box4p = p;

    vec2 cell = pMod2(p.xz, vec2(size));
    

    p.x += (noise(cell) - 0.5) * 7.;
    p.z += (noise(cell) - 0.5) * 12.;
    
    float ground = fPlane(p, vec3(0., -1., 0.), 2.);
    float ground_w = 1. - smoothstep(EPS, EPS + .0, ground);
    vec3 ground_c = vec3(0.42, 0.36, 0.3) * 0.7;
    ground_c -= step(0.97, fract(p * 0.5).x) * 0.02;
    ground_c += step(0.94, fract(p * 0.5).z) * 0.02;
    ground_c += vec3(0.15);
    c += ground_w * ground_c;
    r = min(r, ground);


    pR(roofp.yz, sin(roofp.z * 0.2) * 0.002);
    float roof = fPlane(roofp, vec3(0., 1., 0.), 40. - min(iTime * 1.2, 35.));
    float roof_w = 1. - smoothstep(EPS, EPS + .0, roof);
    vec3 roof_c = vec3(0.2, 0.1, 0.1) * 0.5;
    c += roof_w * roof_c;
    

    vec3 bloodTexturePos = vec3(p.x, p.y - iTime * 15., p.z);
    pR(p.xy, sin((rand(cell.y * cell.x) + 0.4) * 0.8 * bloodTexturePos.y + cell.y * cell.x) * 0.1);
    // float box1 = fBox2(p.xz, vec2(1.5 - iTime * 0.04));
    float box1 = fCylinder(p, noise(cell) * 2. - 3. + iTime * 0.0, 100.);
    box1 += displacement(bloodTexturePos, 0.5 + 1. * noise(cell.xy * 400.) * 0.2) * 0.8;
    box1 += displacement(p, 0.5 + 1. * noise(cell.xy * 400.) * 0.2) * 0.4;
    box1 *= 0.3 + noise(bloodTexturePos * 1. + cell.x + cell.y) * 0.9;
    box1 -= 0. + noise(bloodTexturePos) * 0.7;
    // box1 *= 1. - noise(p * 1. + cell.x + cell.y);
    float box1_w = 1. - smoothstep(EPS, EPS + 0.2 + iTime * 0.02, box1);
    vec3 box1_c = vec3(0.6, 0.1, 0.1);
    box1_c *= 0.6 + noise(bloodTexturePos * 5. + cell.x + cell.y) * 0.4;
    c += box1_w * box1_c;
    // box1 /= step(0.6, rand(cell.x + cell.y));
    // box1 *= 5. - step(1., rand(cell.x + cell.y)) * 4.;

    // r = fOpUnionColumns(r, box1, 0.1 + iTime * 0.02, 1. + iTime * 0.05);
    r = fOpUnionRound(r, box1, 0.2);

    r = fOpUnionRound(r, roof, 1.8);


    // c = vec3(0.4);

    c = clamp(c, 0., 1.);
    return vec4(c, r);
}

float overlay(vec2 p)
{
    float ltrbox = step(0.35, abs(p.y));
    float vignette = 
        smoothstep(0.5, 1.2, abs(p.x)) + 
        smoothstep(0.2, 0.6, abs(p.y));
    float ambient = sin(iTime * 0.4) * 0.05;
    
    return 0. -ltrbox -vignette*0.6 +ambient;
}

// 64k ??
void main()
{
    float panic = 1. + iTime * 0.05;
    vec2 uv = (gl_FragCoord.xy - (iResolution.xy * 0.5)) / iResolution.yy;
    float ol = overlay(uv);
    // uv += (vec2(rand(iTime), rand(iTime + 1.0)) - 0.5) * iTime * 0.0004; // shake
    vec3 cameraOrigin = vec3(0., 0.5, 2.);
    cameraOrigin += vec3(sin(iTime * 3. * panic) * 3., sin(iTime * 0.4 * 3.* panic) * 0.5, sin(iTime * 3.* panic) * 1.) * 0.01 * panic; // gungis
    vec3 cameraTarget = vec3(0., 0., -10.);
    vec3 upDir = vec3(.0, 1.0, .0);
    pR(upDir.yx, (sin(iTime * 2.3)) * 0.02); // gungis
    vec3 cameraDir = normalize(cameraTarget - cameraOrigin);
    vec3 cameraRight = normalize(cross(upDir, cameraOrigin));
    vec3 cameraUp = cross(cameraDir, cameraRight);
    vec3 rayDir = normalize(cameraRight * uv.x + cameraUp * uv.y + cameraDir);
    float dist = EPS;
    float totalDist = 0.0;
    vec3 p = cameraOrigin;
    vec3 c = vec3(0.);

    for(int i = 0; i < MAX_ITERATIONS && dist >= EPS && totalDist < MAX_DIST; i++)
    {
        vec4 result = scene(p);
        c = result.rgb;
        dist = result.w;

        totalDist += dist;
        p += dist * rayDir;
    }

    if(dist < EPS)
    {
        vec2 eps = vec2(0.0, EPS);
        vec3 normal = normalize(vec3(
            scene(p + eps.yxx).a - scene(p - eps.yxx).a,
            scene(p + eps.xyx).a - scene(p - eps.xyx).a,
            scene(p + eps.xxy).a - scene(p - eps.xxy).a));

        vec3 lightOffset = vec3(0., 0., 0.6);
        float diffuse = max(.2, dot(-normalize(rayDir + lightOffset) * 1., normal * 1.));
        float specular = pow(diffuse, 10.);
        c *= min(1., diffuse + specular);
    }
    
    vec3 bgc = vec3(0.15);
    float bgc_w = min(1., totalDist / MAX_DIST);
    float c_w = 1. - bgc_w;

    c = c * c_w + bgc * bgc_w;

    c += noise(floor(uv * 100.), 0.005, floor(iTime * 10.)) * vec3(1., 0., 0.); // rain
    c += noise(floor(uv * 10.), 0.003, floor(iTime * 10.)) * vec3(1., 0., 0.); // rain
    c *= computeFog(cameraOrigin, rayDir, vec3(0., 0., -3.), 12. - iTime * 0.05, 32) * 1.1; // vinjett

    c *= 1. - step(27.51, iTime);
    // c *= 0.95;
    c *= min(1., iTime * 0.7); // fade in
    c *= 1. - smoothstep(15., 18, iTime); // fade out

    c *= 1. + ol;

    c += (0.5 - noise(uv, 0.5, iTime)) * 0.008; // demoire noise
    c *= 1.5;
    fragColor = vec4(c, 1.);
}
