#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 9000
#define AO 6
#define STEPS 60
#define TERRAIN_COMPLEXITY 6.
float time=iGlobalTime;
float t = mod(-0.3*time, 3.0*3.14159);

// 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 );
}

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

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.1)-time*0.4)/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.001)+cos(-t+p.x*0.001))*0.4);
    float d = 5.*tnoise(p.xz) + p.y+400.0 + ter * 900.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;
}

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

vec3 normal(vec3 p){
	float e=6.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;
}

vec3 lightpos   = vec3(-7000,3000,-3500);
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(.91,.91,.91);
	vec3 shadowcolor = vec3(0);//vec3(1)-lightcolor;
	vec3 fogcolor    = vec3(0.0);

	//vec3 amb = vec3( pow(ao(point, norm), 1.0) );

    //return lightcolor*intensity;
    
    float fd = 70.0*no(vec3(0.003*point.xzy)+time)-20.0*no(vec3(0.008*point.xzy)+time);
    fd += pow(dist, 2.0)*0.000024;
	return mix(lightcolor*intensity*mix(shadowcolor,vec3(1),shadows),vec3(0.8),clamp(-1.2-.0024*(point.y-fd),0.0,1.0));
}

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(1);
    //return vec3(steps/float(STEPS));
	return shade(position, step);
}

vec3 correct(vec3 c){
    c = c*1.1-0.05;
	c = smoothstep(.0, .95, c);
    c = smoothstep(-0.175, 1.8, c);
    return 1.1*vec3(length(c));
}

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

	// camera setup
	mat3 angle     = rx(0.15)*ry(1.8)*rz(3.3);
    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);*/
    float st = iGlobalTime*0.8;
    mat3 shake = rx(.1*snoise( vec3(st*1.14) ))*
                 ry(.1*snoise( vec3(st*0.71) ))*
                 rz(.05*snoise( vec3(st*1.99) ));
        
	vec3 camera    = vec3(0);
	vec3 direction = normalize(vec3(p, 0.8)*angle*rotation*shake);
	
	// render visuals
	vec3 color = march(camera, direction);
	
	// correct, post and output
	color = correct(color);
	color = pow(color, vec3(1.0/2.2));
	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);
}