#version 330 core
in vec2 UV;
out vec4 fragColor;
uniform sampler2D iChannel0; // Noise tex
uniform vec2 iResolution;
uniform float iGlobalTime;



float E = 0.1;
#define FAR_CLIP 14000
#define AO 6
#define STEPS 60
#define TERRAIN_COMPLEXITY 6.
float time=iGlobalTime;
float t = mod(-0.3*time, 10.0*3.14159);
const vec3 campos = vec3(-1400,500,-100);

// various helper functions
float imp(float k,float x){return k*x*exp(1.0-k*x);}
// rotation matrixes
mat3 rx(float a){return mat3(1.0,0.0,0.0,0.0,cos(a),-sin(a),0.0,sin(a),cos(a));}
mat3 ry(float a){return mat3(cos(a),0,sin(a),0.0,1.0,0.0,-sin(a),0.0,cos(a));}
mat3 rz(float a){return mat3(cos(a),-sin(a),0.0,sin(a),cos(a),0.0,0.0,0.0,1.0);}
// white noise
float hash(float c){return fract(sin(dot(c,12.9898))*43758.5453);}

float no(vec3 x){
	vec3 p=floor(x);
	vec3 f=fract(x);

	f=f*f*(3.0-2.0*f);
	float n=p.x+p.y*57.0+113.0*p.z;
	return mix(mix(mix(hash(n),hash(n+1.0),f.x),
				mix(hash(n+57.0),hash(n+58.0),f.x),f.y),
			mix(mix(hash(n+113.0),hash(n+114.0),f.x),
				mix(hash(n+170.0),hash(n+171.0),f.x),f.y),f.z);
}

float snoise(vec3 x) {
    vec3 p = floor(x);
    vec3 f = fract(x);
	f = f*f*(3.0-2.0*f);
	
	vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy;
	vec2 rg = texture2D( iChannel0, (uv+ 0.5)/256.0, -100.0 ).yx;
	return mix( rg.x, rg.y, f.z );
}

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;
    const float K2 = 0.211324865;
	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 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) );
}

const mat2 m = mat2( 0.80,  0.60, -0.60,  0.80 );
float fbm4( in vec2 p ){
    float f = 0.0;
    f += 0.5000*noise( p ); p = m*p*2.02;
    f += 0.2500*noise( p ); p = m*p*2.03;
    f += 0.1250*noise( p ); p = m*p*2.01;
    f += 0.0625*noise( p );
    return f;
}

float fbm6( in vec2 p ){
    float f = 0.0;
    f += 0.5000*noise( p ); p = m*p*2.02;
    f += 0.2500*noise( p ); p = m*p*2.03;
    f += 0.1250*noise( p ); p = m*p*2.01;
    f += 0.0625*noise( p ); p = m*p*2.04;
    f += 0.031250*noise( p ); p = m*p*2.01;
    f += 0.015625*noise( p );
    return f;
}

float turb4( in vec2 p ){
    float f = 0.0;
    f += 0.5000*abs(noise(p)); p = m*p*2.02;
    f += 0.2500*abs(noise(p)); p = m*p*2.03;
    f += 0.1250*abs(noise(p)); p = m*p*2.01;
    f += 0.0625*abs(noise(p));
    return f;
}

float turb6( in vec2 p ){
    float f = 0.0;
    f += 0.5000*abs(noise(p)); p = m*p*2.02;
    f += 0.2500*abs(noise(p)); p = m*p*2.03;
    f += 0.1250*abs(noise(p)); p = m*p*2.01;
    f += 0.0625*abs(noise(p)); p = m*p*2.04;
    f += 0.031250*abs(noise(p)); p = m*p*2.01;
    f += 0.015625*abs(noise(p));
    return f;
}

float marble(in vec2 p){
	return cos(p.x+fbm4(p));
}

float wood(in vec2 p){
	float n = noise(p);
	return n-floor(n);
}

float dowarp ( in vec2 q, out vec2 a, out vec2 b ){
	float ang=0.;
	ang = 0.31*iGlobalTime;
	mat2 m1 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));
	ang = -0.59*iGlobalTime;
	mat2 m2 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));

	a = vec2( marble(m1*q), marble(m2*q+vec2(1.12,0.654)) );

	ang = 0.63*iGlobalTime;
	m1 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));
	ang = -0.7*iGlobalTime;
	m2 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));

	b = vec2( marble( m2*(q + a)), marble( m1*(q + a) ) );
	
	return marble( q + b +vec2(0.32,1.654));
}

vec3 cpat(vec2 q){
	vec2 a = vec2(0.);
	vec2 b = vec2(0.);
	float f = dowarp(q, a, b);
	f = 0.5+0.5*f;
	vec3 col = vec3(f);
	float c = 0.;
	c = f;
	col = vec3(c, c*c, c*c*c);
	c = abs(a.x);
	col -= vec3(c*c, c, c*c*c);
	c = abs(b.x);
	col += vec3(c*c*c, c*c, c);
	col *= 0.7;
	col.x = pow(col.x, 2.18);
//	col.y = pow(col.y, 1.58);
	col.z = pow(col.z, 1.88);
	col = smoothstep(0., 1., col);
	col = sqrt(col);
	return clamp(col, 0., 1.);	
}
*/

vec3 cpat(vec2 q){
 	return vec3(smoothstep(0.0, 20.0, q.x+q.y),
                smoothstep(0.0, 12.0, q.x+q.y),
                smoothstep(0.0, 12.0, q.x+q.y))
           * exp(2.0-length(q.xy));
}

//////////////////////////////////////////////////////////////////////////////////////////

float tri(in float x){
    return abs(fract(x)-0.5);
}
mat2 m2 = mat2( 0.80,  0.60, -0.60,  0.80 );
float tnoise(in vec2 p){
    p*=.001;
    float z=2.0;
	float rz = 0.;
	for (float i= 1.;i < TERRAIN_COMPLEXITY;i++ )
	{
        rz+= tri(p.x+tri(p.y*1.0)-time*0.7)/z;
		z = z*2.;
		p = p*1.8;
        p*= m2;
	}
	return rz*100.;
}
float oct(in vec3 p){ return dot(vec3(0.5773),abs(p));}
float ou( float d1, float d2 ){return (d1<d2) ? d1 : d2;}
vec3 roty(vec3 p, float a){
    float s = sin(a), c = cos(a);
    return vec3(c*p.x + s*p.z, p.y, -s*p.x + c*p.z);
}

float geometry(vec3 p){
    float ter = tri((sin(t+p.z*0.0015)+cos(-t+p.x*0.0015))*0.4);
    float d = 5.*tnoise(p.xz) + p.y+400.0 + ter *800.0;
    d -= abs(p.x*0.5*log(abs(p.x)))*0.05-8.;
    float rocket_param2 = 750.0;
    //d = ou(d, p.y+rocket_param2);
    return d;
}

////////////////////////////////////////////////////////////////////////////////////////////////

/*
float geometry(vec3 p){
    float am = 0.6;
    float noiseterm = am*snoise( 5.0*p+vec3(0,0.5*(iGlobalTime*3.0),0) ) - am*0.5;
    //float noiseterm = am*no( 5.0*p+vec3(0,0.5*(iGlobalTime*4.0),0) ) - am*0.5;
    float f = bx(p+vec3(9,-1.5-noiseterm,9), vec3(9,1,9) );

   	return f;
}
*/
vec3 normal(vec3 p){
	float e=50.0;
	return normalize(vec3(geometry(p+vec3(e,0,0))-geometry(p-vec3(e,0,0)),
	                      geometry(p+vec3(0,e,0))-geometry(p-vec3(0,e,0)),
	                      geometry(p+vec3(0,0,e))-geometry(p-vec3(0,0,e))));
}

float shadow(vec3 ro, vec3 rd, float mint, float maxt, float k){
    float rocket_param1 = 0.1;
	float res=1.0;
    float t = mint;
    for(int i = 0; i < 40; i++){
		float h = geometry(ro+rd*t);
		if(h < rocket_param1) return 0.0;
		res=min(res,k*h/t);
		t+=h;
	}
	return res;
}

float ao(vec3 spoint, vec3 norm){
    float ao = 6.0;
	float occlusion = ao;
	float factor = .6;
	for(int i = 0; i < 6; ++i){
		spoint += .06*norm;
		occlusion -= geometry(spoint)*factor;
		factor *= .6;
	}
	return clamp((ao*ao)*(1.0-occlusion/ao), 0.0, 1.0);
}

vec3 lightpos   = vec3(-6000,2000,-3000);
vec3 shade(vec3 point, float dist){
    //lightpos.xz *= 1500.0*vec2(sin(time*0.91), cos(time*1.07));
	vec3 norm = normal(point);
    //return norm;
	vec3 lightdir   = -normalize(point - lightpos);
	float intensity = 0.15+0.85*max(0.0, dot(lightdir, norm) );

	float shadows   = shadow(point, lightdir, 20.0, 2000.0, 3.0);

	vec3 lightcolor  = vec3(1);
	vec3 shadowcolor = vec3(0);
    
    float pf = 0.25;
    // float pattern = 0.05*pow(0.3+sin(point.x*pf)+cos(point.z*pf), 9.0);
    float pattern = float(mod(0.02*point.y, 1.0) > 0.15);
    //pattern = mix(pattern, 0.1, dist/5000.0);
    
    vec3 col = vec3(pattern) * exp(1.0-0.0006*dist); //*mix(cpat(point.xz*0.002+3.0)*0.9, vec3(0.5), 0.05);
    //vec3 col = pattern*cpat(point.xz*0.001);
    
	return intensity*mix(shadowcolor,vec3(1),shadows)*col;
}

vec3 march(vec3 origin, vec3 direction){
	vec3 position = vec3(0.0);
	float dist = 0.0;
	float step = 0.0;
	float glow = 0.0;
    
    float steps = 0.0;
    
	for(int i=0; i < STEPS; ++i){
		position = origin + direction * step;
		dist = geometry(position);
		step  += dist;
        
		//glow += 0.1*pow(distance,1.25)/geometry(position);
        
        steps = float(i);
		if(float(FAR_CLIP) < step || dist < E) break;
        //E *= 1.0+0.00002*distance(vec3(-4,0,-1), position);
	}

    //float glowvalue = 0.5*clamp(glow/length, 0.0, 1.0);
    
    if(step > float(FAR_CLIP)) return vec3(0);
    //return vec3(steps/float(STEPS));
	return shade(position, step);
}

vec3 correct(vec3 c){
    
    c = c*1.1-0.05;
	c = smoothstep(0.0, 1.0, c);
	//c.z = .03+pow(c.z, 1.3)*1.2;
	//c.x = pow(c.x,.8);
	//return .07+1.8*c;
	
    c = smoothstep(-0.03, 0.8, c);
    
    return 1.5*c;
}

void main() {
    vec2 r = iResolution.xy;
	vec2 p = 1.-2.*UV;
    p.y *= r.y/r.x;

	// camera setup
	mat3 angle     = rx(0.15)*ry(1.8)*rz(-2.7);
    mat3 rotation  = rz(0.5*3.141-3.141*0.4)*ry( 2.0*(3.141+3.141*1.0) );
    /*mat3 shake = rx(.015*texture2D(iChannel0, vec2(iGlobalTime*0.0074)).x)*
                 ry(.015*texture2D(iChannel0, vec2(iGlobalTime*0.0061)).y)*
                 rz(.015*texture2D(iChannel0, vec2(iGlobalTime*0.0059)).z);*/
    mat3 shake = rx(.1*snoise( vec3(iGlobalTime*1.14) ))*
                 ry(.1*snoise( vec3(iGlobalTime*0.71) ))*
                 rz(.05*snoise( vec3(iGlobalTime*1.99) ));
        
	vec3 camera    = campos - 100.0*vec3(iGlobalTime, -0.25*iGlobalTime, iGlobalTime);
	vec3 direction = normalize(vec3(p, 0.7)*angle*rotation*shake);
	
	// render visuals
	vec3 color = march(camera, direction);
	
	// correct, post and output
	color = correct(color);
	color = pow(color, vec3(1.0/2.6));
	float vignette = 1.1 / (1.1 + 1.1*dot(p, p));
	float noise = .05*vec3(hash(length(p)*time)).x;
	fragColor = vec4(color*vignette-noise, 1.0);
}
