uniform sampler2D iChannel0;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform vec2 iResolution;
uniform float iGlobalTime;
uniform float fft;
uniform float cameraRotation_x;
uniform float cameraRotation_y;
uniform float cameraRotation_z;
uniform float cameraPosition_x;
uniform float cameraPosition_y;
uniform float cameraPosition_z;
uniform float pawn_x;
uniform float pawn_y;
uniform float pawn_z;
#define MAX_STEPS 50.
#define MAX_DISTANCE 40.
#define epsilon 0.0001
#define infinite 1e7

mat3 lookat(vec3 fw,vec3 up) {
    fw=normalize(fw);vec3 rt=normalize(cross(fw,normalize(up)));return mat3(rt,cross(rt,fw),fw);
}
mat3 rotationMatrix(vec3 axis, float angle) {
    axis = normalize(axis);
    float s = sin(angle);
    float c = cos(angle);
    float oc = 1.0 - c;
    
    return mat3(oc * axis.x * axis.x + c,           oc * axis.x * axis.y - axis.z * s,  oc * axis.z * axis.x + axis.y * s,
                oc * axis.x * axis.y + axis.z * s,  oc * axis.y * axis.y + c,           oc * axis.y * axis.z - axis.x * s,
                oc * axis.z * axis.x - axis.y * s,  oc * axis.y * axis.z + axis.x * s,  oc * axis.z * axis.z + c);
}
float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
float rand(float co){
    return rand(vec2(co));
}
float smin( float a, float b, float k ) {
    float res = exp( -k*a ) + exp( -k*b );
    return -log( res )/k;
}
float sdSphere( vec3 p, float s ){
  return length(p)-s;
}
float udRoundBox( vec3 p, vec3 b, float r ) {
  return length(max(abs(p)-b,0.0))-r;
}
float udBox( vec3 p, vec3 b )
{
  return length(max(abs(p)-b,0.0));
}
float sdBox( vec3 p, vec3 b )
{
  vec3 d = abs(p) - b;
  return min(max(d.x,max(d.y,d.z)),0.0) +
         length(max(d,0.0));
}
float sdHexPrism( vec3 p, vec2 h )
{
    vec3 q = abs(p);
    q=q.zxy;
    return max(q.z-h.y,max((q.x*0.866025+q.y*0.5),q.y)-h.x);
}

float length2( vec2 p ) {
	return sqrt( p.x*p.x + p.y*p.y );
}

float length6( vec2 p ) {
	p = p*p*p; p = p*p;
	return pow( p.x + p.y, 1.0/6.0 );
}

float length8( vec2 p ) {
	p = p*p; p = p*p; p = p*p;
	return pow( p.x + p.y, 1.0/8.0 );
}
float sdTorus88( vec3 p, vec2 t )
{
  vec2 q = vec2(length8(p.xz)-t.x,p.y);
  return length8(q)-t.y;
}
float sdTorus82( vec3 p, vec2 t )
{
  vec2 q = vec2(length2(p.xz)-t.x,p.y);
  return length8(q)-t.y;
}
float sdCappedCylinder( vec3 p, vec2 h )
{
  vec2 d = abs(vec2(length(p.xz),p.y)) - h;
  return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
float sdCapsule( vec3 p, vec3 a, vec3 b, float r )
{
    vec3 pa = p - a, ba = b - a;
    float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
    return length( pa - ba*h ) - r;
}
mat3 rotY(in float a)
{
	return mat3( cos(a), 0.0, sin(a),
				 0.0,    1.0, 0.0,
				-sin(a), 0.0, cos(a)
			    );
}

mat2 rotate(float Angle)
{
    mat2 rotation = mat2(
        vec2( cos(Angle),  sin(Angle)),
        vec2(-sin(Angle),  cos(Angle))
    );
	return rotation;
}

vec2 hash( vec2 p )
{
	p = vec2( dot(p,vec2(127.1,311.7)),
			  dot(p,vec2(269.5,183.3)) );

	return -1.0 + 2.0*fract(sin(p)*43758.5453123);
}

float noise( in vec2 p )
{
    const float K1 = 0.366025404; // (sqrt(3)-1)/2;
    const float K2 = 0.211324865; // (3-sqrt(3))/6;

	vec2 i = floor( p + (p.x+p.y)*K1 );
	
    vec2 a = p - i + (i.x+i.y)*K2;
    vec2 o = (a.x>a.y) ? vec2(1.0,0.0) : vec2(0.0,1.0); //vec2 of = 0.5 + 0.5*vec2(sign(a.x-a.y), sign(a.y-a.x));
    vec2 b = a - o + K2;
	vec2 c = a - 1.0 + 2.0*K2;

    vec3 h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 );

	vec3 n = h*h*h*h*vec3( dot(a,hash(i+0.0)), dot(b,hash(i+o)), dot(c,hash(i+1.0)));

    return dot( n, vec3(70.0) );
	
}
float sdTorus( vec3 p, vec2 t ) { vec2 q = vec2(length(p.xz)-t.x,p.y); return length(q)-t.y; }
float getColorReflection( vec3 position, out vec3 color, out float reflectiveValue) {

    vec3 _position = position;
    vec3 _position2 = _position;
    _position -= vec3( pawn_x, pawn_y, pawn_z);
    _position *= rotationMatrix(vec3(1.0,0.4,0.6),iGlobalTime*2.);
    vec3 _position3 = _position;
    float dist = 1e8;
    float time = iGlobalTime;
    float t2 = iGlobalTime*.1111;
    float h=0.;
    _position.y*=0.8;
    dist = sdHexPrism(_position,vec2(.9+sin(_position.y+3.141592*0.9)/2.,2.1));
    dist = min(dist, sdSphere(_position3-vec3(0.,3.4,0.),0.9));
    dist = min(dist, sdTorus82(_position-vec3(0.,-1.7,0.),vec2(1.6-_position.y/12.,0.2)));
    dist = max(dist, -sdTorus(_position-vec3(0.,-1.2,0.),vec2(1.5,0.2)));
    dist = min(dist, sdCappedCylinder(_position-vec3(0., 1.6,0.),vec2(1.1+sin(_position.y*4.)/12.,0.2)));
    float dist2 = sdBox(_position2-vec3(0.,-6.9,0.),vec3(212.,1.,212.));
    dist2 = min(dist2, sdBox(_position2-vec3(0.,8.9,0.),vec3(122.,1.,212.)));
    
    color=vec3(.9);
    if(dist2<dist) {
        dist=dist2;
        color=vec3(1.)*mod(floor(_position2.x/3.)+floor(_position2.z/3.),2.);
    }
    return dist;
}
float getDistance( in vec3 position) {
    vec3 _position = position;
    vec3 c;
    float r;
    return getColorReflection(_position, c, r);
}

vec3 getSurfaceNormal(vec3 position) {
    float e=epsilon;
    vec3 normalVector = vec3(
        					getDistance(position+vec3( e,  0., 0.)) - getDistance(position+vec3(  -e,   0.,  0.)) ,
        					getDistance(position+vec3( 0., e,  0.)) - getDistance(position+vec3(   0., -e,   0.)) ,
        					getDistance(position+vec3( 0., 0., e)) - getDistance(position+vec3(    0.,  0., -e)) );
    normalVector = normalize(normalVector);
    return normalVector;
}
float traceToLight(vec3 rayPosition, vec3 normalVector, vec3 lightSource, float raylightdist){

    vec3 ro = rayPosition;
    vec3 rd = normalize(lightSource - rayPosition);
    float t = 0.04;
    float k = distance(lightSource, rayPosition)/4.;
    float res = 1.0;
    for( int i=0; i<44; i++ )
    {
        float h = getDistance(ro + rd*t);
        h = max( h, 0.0 );
        res = min( res, k*h/t );
        t += clamp( h, 0.001, 0.9 );
        if( h<0.01) break;
    }
    return clamp(res,0.07,8.0);
}
vec3 rhs(vec3 dir, float i)
{
	vec2 rnd = vec2(rand(i+1.), rand(i+2.));
	float s = rnd.x*3.141592*2.;
	float t = rnd.y*2.-1.;
	vec3 v = vec3(sin(s), cos(s), t) / sqrt(1.0 + t * t);
	return v * sign(dot(v, dir));
}

float ao( vec3 p, vec3 n, float maxDist, float falloff)
{
    vec3 c;
    float r;
	float ao = 0.0;
	for( int i=0; i<2; i++ )
	{
		float l = rand(float(i))*maxDist;
		vec3 rd = normalize(n+rhs(n, l )*0.95)*l;
		ao += (l - getColorReflection( p + rd , c, r)) / pow(1.+l, falloff);
	}
	return clamp(1.-ao*2.,0.0,1.0);
}

vec3 shade( vec3 p, vec3 n, vec3 org, vec3 dir )
{
	return vec3(1.2)*sqrt(mix(ao(p,n, 4.0, 0.9), ao(p,n, 0.9, 1.5), 0.2));
}
vec4 march(vec3 rayOrigin, vec3 rayDirection) {
    vec2 p = gl_FragCoord.xy / iResolution.xy;
	float aspectCorrection = (iResolution.x/iResolution.y);
	vec2 coordinate_entered = 2.0 * p - 1.0;
	vec2 coord = vec2(aspectCorrection,1.0) *coordinate_entered;
    vec3 origRayDirection=rayDirection;
    vec3 endColorResult = vec3(0.0);
    vec3 prevPosition = rayOrigin;
	vec3 curPosition = rayOrigin + rayDirection * 0.001;
    vec3 normalVector;
    vec4 result=vec4( 0., 0., 0., 0.);
    vec3 finalLight = vec3(0.0);
	float dist = 0.0;
    float storeColorStrength = 1.0;
    int hit = 0;
    float collisions = 0.;
    float distanceOfCollision = 1111110.;
    float distanceOfHit = 0.;
    
        for(float i = 0.; i < MAX_STEPS; i++) {
            float R = 0.0;
            vec3 color = vec3( 0.0);
            float stepable = getColorReflection( curPosition, color, R);
            dist += stepable;
            curPosition = prevPosition + dist * rayDirection;
           	/*too far from begining point, call it an end*/
            if(length(curPosition)>MAX_DISTANCE) break;
            if( abs( stepable) < epsilon+i/1000.) {
                vec3 normalVector = getSurfaceNormal( curPosition);
                endColorResult = color;
   				vec3 AOresult = shade(curPosition,normalVector,origRayDirection, rayDirection );
                endColorResult *=  AOresult;
                distanceOfHit = dist;
                
                hit = 1;
                break;
            }
    }
    if(hit==0) {
        curPosition /= 10.;
        curPosition += vec3(min(floor(mod(abs(coord.x+coord.y+iGlobalTime + floor(coord.x*4.+sin(iGlobalTime+coord.x))/4. )*12.,4.)),1.));
   		curPosition += floor(curPosition*12121121.);
    }
    
    return vec4( vec3(1.)*mod(distance(rayOrigin,curPosition)/48.,1.)+vec3(111.), distanceOfHit);
}

vec4 render( vec2 uv ) {  
     float aspect = iResolution.x / iResolution.y;
    	float T=iGlobalTime*2.2;
        vec3 cameraPosition = vec3( cameraPosition_x, cameraPosition_y, cameraPosition_z+fft*1000.);
        vec3 direction = normalize( vec3(.5 * uv * vec2( aspect, 1.0), 0.7) );
        direction.z *= -1.0;
        direction *= lookat( normalize( cameraPosition-vec3( pawn_x,-pawn_y, pawn_z) ),vec3(0.,1.,0.));
        direction *= rotationMatrix(vec3(1.,0.,0.),cameraRotation_x);
        direction *= rotationMatrix(vec3(0.,1.,0.),cameraRotation_y);
        direction *= rotationMatrix(vec3(0.,0.,1.),cameraRotation_z);
        vec4 color = march( cameraPosition, direction);

        color.xyz = mod(color.xyz,vec3(1.));
        return color;
        
}

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {  
     vec2 p = gl_FragCoord.xy / iResolution.xy;
     vec2 uv = 2.0 * gl_FragCoord.xy / iResolution.xy - 1.0;
     float aspect = iResolution.x / iResolution.y;
     vec4 C=render( uv );
     fragColor =  vec4(C.rgb,min(C.w/3000.,.0075));
    
}


void main() {
    vec4 fragColor = vec4( 0.);
    vec2 fragCoord = gl_FragCoord.xy;
    mainImage( fragColor, fragCoord);
    gl_FragColor = fragColor;
}
    