#define pi 3.14
#define MOD2 vec2(3.07965, 7.4235)

float hash( float p )
{
	vec2 p2 = fract(vec2(p) / MOD2);
    p2 += dot(p2.yx, p2.xy+19.19);
	return fract(p2.x * p2.y);
}

float hash(vec2 p)
{
	p  = fract(p / MOD2);
    p += dot(p.xy, p.yx+19.19);
    return fract(p.x * p.y);
}

float noise( in vec2 x )
{
    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f*f*(3.0-2.0*f);
    float n = p.x + p.y*57.0;
    float res = mix(mix( hash(n+  0.0), hash(n+  1.0),f.x),
                    mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y);
    return res;
}

mat3 create_camera( in vec3 ro, in vec3 ta, float cr )
{
	vec3 cw = normalize(ta-ro);
	vec3 cp = vec3(sin(cr), cos(cr),0.0);
	vec3 cu = normalize( cross(cw,cp) );
	vec3 cv = cross(cu,cw);
    
    return mat3( cu, cv, cw );
}

float sd_sphere( vec3 p, float s )
{
    return length(p)-s;
}

float sd_box( vec3 p, vec3 b )
{
  vec3 d = abs(p) - b;
  return length(max(d,0.0)) + min(max(d.x,max(d.y,d.z)),0.0);
}

float sd_octahedron(vec3 p, float s ) 
{
    p = abs(p);
    return (p.x + p.y + p.z - s)*0.57735027;
}

float bsin(float v)
{
    return sin(v) * 0.5 + 1.0;
}

float bcos(float v)
{
    return cos(v) * 0.5 + 1.0;
}

float op_union( float d1, float d2 )
{
    return min(d1,d2);
}

float map(vec3 p)
{
	float scale = 10.0;
    float rep = 2.0;
    float pyr_rep = 300.0;
    
    float y = noise(p.xz / 20.0) * 8.0;
    
    vec3 q = mod(p - vec3(0.0, y, 0.0), vec3(rep, 0.0, rep)) - 0.5 * rep;
    vec3 b = mod(p / vec3(1.0, 10.0, 1.0), vec3(0.0, 0.0, 200.0)) - 0.5 * vec3(0.0, 0.0, 200.0);
    vec3 c = mod(p, vec3(0.0, 0.0, pyr_rep)) - 0.5 * vec3(0.0, 0.0, pyr_rep);
    
 	float terrain = sd_box(q - vec3(0.0, 5.0, 0.0), vec3(2.5));
    float pillars_left = sd_box(b - vec3(40.0, 0.0, 0.0), vec3(10.0));
    float pillars_right = sd_box(b - vec3(-40.0, 0.0, 0.0), vec3(10.0));
    float pyramids_left = sd_octahedron(c - vec3(-400.0, 0.0, 0.0), 100.0);
    float pyramids_right = sd_octahedron(c - vec3(400.0, 0.0, 0.0), 100.0);
    
    float res = op_union(terrain, pillars_left);
    res = op_union(res, pillars_right);
    res = op_union(res, pyramids_left);
    res = op_union(res, pyramids_right);
        
    return res;
}

vec3 sky(vec3 v)
{
    vec3 grad_a = vec3(0.5, 0.5, 0.0);
    vec3 grad_b = vec3(0.5, 0.0, 1.0);
    
    grad_a = vec3(bcos(iTime), 0.2, bcos(-iTime));
    grad_b = vec3(bsin(iTime), bsin(-iTime), 0.2);
    
    float grad_t = v.y * 0.5 + 0.5;
    
    return mix(grad_b, grad_a, grad_t);
}

vec3 calc_normal(in vec3 pos)
{
    vec3 eps = vec3(0.001, 0.0, 0.0);
    vec3 nor;
    
    nor.x = map(pos+eps.xyy) - map(pos-eps.xyy);
    nor.y = map(pos+eps.yxy) - map(pos-eps.yxy);
    nor.z = map(pos+eps.yyx) - map(pos-eps.yyx);
    
    return normalize(nor);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    float eps = 0.005;
    
    vec2 mo = iMouse.xy/iResolution.xy;
	float time = 15.0 + iTime;
    
    float yp = 20.0 + bsin(iTime) * 20.0;
    float xp = -0.0;
    float zp = -10.0 + iTime * 50.0;

    vec3 ro = vec3(xp, yp, zp);
    
    vec3 ta = vec3( xp, yp, zp + 10.0);
    mat3 cam = create_camera( ro, ta, 0.0 );
        
    vec3 col = vec3(0.0, 0.0, 0.0);
    
	vec2 p = (-iResolution.xy + 2.0*fragCoord)/iResolution.y;  
    
    vec3 rd = cam * normalize( vec3(p.xy,2.0) );
    
    float d = 10.0;
    float xt = 0.0;
    vec3 pp = ro;
   
    vec3 l = normalize(vec3(0.0, 1.0, 1.0));
    
    for(float t = 0.0; t < 200.0; ++t)
    {
        pp = ro + rd * xt;
        
        vec3 qq = pp;
        d = map(qq);
        
        if(d < eps)
            break;
        
        xt += d;
    }
    
    vec3 n = calc_normal(pp);
    
    float nv = dot(n, -rd);
    
    vec3 csky = sky(rd);
    vec3 lsky = sky(-rd);
    
    vec3 ir = vec3(1.0, 0.0, 0.0) * lsky;
    vec3 ig = vec3(0.0, 1.0, 0.0) * lsky;
    vec3 ib = vec3(0.0, 0.0, 1.0) * lsky;

    col += sin(nv * ig * 10.0 * 1.5) * 0.5 + 0.5;  
    col += sin(nv * ir * 20.0 * 1.5) * 0.5 + 0.5;  
    col += sin(nv * ib * 5.0 * 1.5) * 0.5 + 0.5;
    col = clamp(normalize(col), 0.0, 1.0);
    
    float mask = step(d, eps);
    float inv_mask = 1.0 - mask;
    
    fragColor = vec4(csky * inv_mask + col * mask, 1.0);
}
