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

#define DUR (5.323173125 / 4.0) // duration
#define REPEATS 4.0
#define TOTALDUR (DUR * REPEATS)
#define SPD 1.0 // timescale
#define OFF 0.1 // time offset for audio timings

const int RM_MAX_ITER = 8;
const int DISTANCE = 10;

const float EPSILON = 0.1;

float rand1d(float n)
{
	return fract(sin(n) * 43758.5453);
}

float time()
{
    return mod((iGlobalTime + OFF) * SPD, DUR * REPEATS) / DUR;
}

float time(float offset)
{
    return (mod((iGlobalTime + OFF) * SPD, DUR * REPEATS) + offset) / DUR;
}

float quart()
{
    return mod(time(), 0.25);
}

float quart(float offset)
{
    return mod(time(offset), 0.25);
}

float halft()
{
    return mod(time(), 0.5);
}

float halft(float offset)
{
    return mod(time(offset), 0.5);
}

float wholet()
{
    return mod(time(), 1.);
}

float wholet(float offset)
{
    return mod(time(offset), 1.);
}

float smin(float a, float b, float k)
{
    float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
    return mix( b, a, h ) - k*h*(1.0-h);
}

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

float texture1(vec2 p)
{
    p *= 0.03;

    float a = sin(p.x) * cos(p.y * 0.9 + 0.4) * 2.;
    float b = cos(p.x * 1.22 + 2.) * sin(p.y * 1.4) * 3.;
    pR45(p.xy);
    float c = cos(p.x * 3.22 + 1.) * sin(p.y * 3.4 + 0.5) * 2.;
    pR45(p.xy);
    float d = tan(p.x * 3.22 + 2.) * cos(p.y * 3.4 + 0.5) * 2.;

    float x = a + b * c + d;

    float val = cos(x * 1.) * 1.0;
    val += sin(c) * 0.2;
    val *= cos(b * 8.) * 0.2 + 1.0;
    return val;
}

float scene(in vec3 p)
{
	mat4 matrix = mat4(
		vec4(cos(p.y + iGlobalTime), 0, sin(p.y + iGlobalTime), 0),
		vec4(0, 1, 0, 0),
		vec4(-sin(p.y + iGlobalTime), 0, cos(p.x + iGlobalTime), 0),
		vec4(0, 0, 0, 1)
	);

    p = (vec4(p, 0.) * matrix).xyz;

    return fBox(p, vec3(1.0));

	// return length(max(abs(matrix * vec4(p, 0.0)) - 1.0, 0.0)) - 0.1;
}

vec3 getNormal(in vec3 p)
{
	vec3 normal;
	vec3 ep = vec3(EPSILON, 0, 0);

	normal.x = scene(p + ep.xyz) - scene(p - ep.xyz);
	normal.y = scene(p + ep.yxz) - scene(p - ep.yxz);
	normal.z = scene(p + ep.yzx) - scene(p - ep.yzx);

	return normalize(normal);
}

void main()
{
    vec2 fragCoord = gl_FragCoord.xy;

	vec2 uv = (fragCoord.xy - (iResolution.xy * 0.5)) / iResolution.yy;

	vec3 rayStart = vec3(uv, DISTANCE);
	vec3 rayDir = vec3(uv, -1.2);

	vec3 p;

	float t = 0.0;

	for (int i = 0; i < RM_MAX_ITER; i++)
	{
		p = rayStart + rayDir * t;

		t += scene(p);
	}

	float value = 1.0 - (t * (t / 2.0)) * 0.0005;

	vec3 normal = getNormal(p);
	vec3 light = vec3(0, 2, 10);
	vec3 color = vec3(0.8, 0.8, 0.8);

	vec3 N = normalize(normal);
	vec3 V = normalize(rayDir);
	vec3 R = reflect(V, N);
	vec3 L = normalize(light);

	vec3 ambient = color * vec3(value);
	vec3 diffuse = color * value * max(dot(L, N), 0.0);
	vec3 specular = vec3(value) * pow(max(dot(R, L), 0.0), 5.0) * 0.3;
	
    vec4 blob = vec4(ambient + diffuse + specular, 1.0) * 1.0;
    
    // float x = fragCoord.x / iResolution.x - 0.5;
    // float y = fragCoord.y / iResolution.y - 0.5;
    // x *= 1350.0;
    // y *= 1.0;
    // float n = iGlobalTime * 2.0;
    // float d = tan(n + (x*cos(y)) * y * 0.01) * 0.1;
    // vec4 pulse = vec4(1.0-d, 1.0-d, 1.0-d, 1.0);
    
    fragColor = blob;
	// fragColor = pulse - blob;
    // fragColor += vec4(0.5);
    
    // fragColor = sqrt(fragColor);
}