#define getNormal getNormalHex
#define FAR 2900.
#define INFINITY 1e32

//#define FOG 1.

float hash12(vec2 p) {
	float h = dot(p,vec2(127.1,311.7));
    return fract(sin(h)*43758.5453123);
}

#define PI 3.14159265
#define PHI (1.618033988749895)

vec3 ro;

#define H(P) fract(sin(dot(P,vec2(127.1,311.7)))*43758.545)
// 	3D noise function (IQ)
float n(vec3 p)
{
	vec3 ip=floor(p);
    p-=ip;
    vec3 s=vec3(1,18,8);
    vec4 h=vec4(0.,s.yz,s.y+s.z)+dot(ip,s);
    // linear
    //p=p*p*(3.-2.*p);
    h=mix(fract(sin(h)*43758.5),fract(sin(h+s.x)*43758.5),p.x);
    h.xy=mix(h.xz,h.yw,p.y);

    return mix(h.x,h.y,p.z);
}

// 	3D noise function (IQ)
float rand(vec3 p){
    float n = sin(dot(p, vec3(234, 7, 157)));
    return fract(length(vec3(123123, 2097152, 262144)*n));
}

float Noise3D(vec3 p)
{
    vec2 e = vec2(0.0, 1.0);
    vec3 i = floor(p);
    vec3 f = fract(p);

    float x0 = mix(rand(i + e.xxx), rand(i + e.yxx), f.x);
    float x1 = mix(rand(i + e.xyx), rand(i + e.yyx), f.x);
    float x2 = mix(rand(i + e.xxy), rand(i + e.yxy), f.x);
    float x3 = mix(rand(i + e.xyy), rand(i + e.yyy), f.x);

    float y0 = mix(x0, x1, f.y);
    float y1 = mix(x2, x3, f.y);

    float val = mix(y0, y1, f.z);

    val = val * val * (3.0 - 2.0 * val);
    return val;
}

float SmoothNoise(vec3 p)
{
    float amp = 1.0;
    float freq = 1.0;
    float val = 0.0;

    for (int i = 0; i < 4; i++)
    {
        amp *= 0.5;
        val += amp * Noise3D(freq * p - float(i) * 11.7179);
        freq *= 2.0;
    }

    return val;
}

#define fromRGB(a, b, c) vec3(F(a), F(b), F(c)) / 255.;

vec3
    light = vec3(0. ,0., 1.),
	lightDir;

vec4 lightColour = normalize(vec4(0.0, .0, .0, 1.) );

float saturate(float a) { return clamp(a, 0.0, 1.0); }

void pR(inout vec2 p, float a) {
	p = cos(a)*p + sin(a)*vec2(p.y, -p.x);
}

float opU2( float d1, float d2 ) {
    if (d1 < d2) return d1;
    return d2;
}

struct geometry {
    float dist;
    float materialIndex;
    float specular;
    float diffuse;
    vec4 color;
    vec3 space;
    float mirror;
    float i;
    float glow;
};

geometry geoU(geometry g1, geometry g2) {
    if (g1.dist < g2.dist) return g1;
    return g2;
}

float opS2( float d1, float d2 ){
    if (-d2 > d1) return -d2;
    return d1;
}

vec3 opI2( vec3 d1, vec3 d2 ) {
 	if (d1.x > d2.x) return d1;
    return d2;
}

// Maximum/minumum elements of a vector
float vmax(vec2 v) {
	return max(v.x, v.y);
}

float vmax(vec3 v) {
	return max(max(v.x, v.y), v.z);
}

// Sign function that doesn't return 0
float sgn(float x) {
	return (x<0.)?-1.:1.;
}

vec2 sgn(vec2 v) {
	return vec2((v.x<0.)?-1.:1., (v.y<0.)?-1.:1.);
}

// Repeat in two dimensions
vec2 pMod2(inout vec2 p, vec2 size) {
	vec2 c = floor((p + size*0.5)/size);
	p = mod(p + size*0.5,size) - size*0.5;
	return c;
}

// Box: correct distance to corners
float fBox(vec3 p, vec3 b) {
	vec3 d = abs(p) - b;
	return length(max(d, vec3(0))) + vmax(min(d, vec3(0)));
}

geometry map(vec3 p) {
    vec3 bp = p;

    geometry box, s;

    float nn = n(vec3(p.x * abs(.01 ), p.z * 0.01, 0.)) * (max(4., abs(p.x * .01)));
    //nn = min(nn, 8.);
    nn *= smoothstep(0., 1., iTime) / 2.;
    nn *= smoothstep(0., 1., abs(p.x) * .002);

    nn *= sin(p.x * .004);

    p.y += 71.;
    p.y *= .46;
    p.y -= mix(0., 50., nn);

    s.dist = fBox(p, vec3(1e9, .2, 1e9));
    s.color = vec4(0.0, 0., .0,0.);

    s.specular = .5;
    s.diffuse = 1.5;
    s.materialIndex = 0.;
    s.glow = 0.;

    bp.x += 175.;

    vec2 gr = pMod2(bp.xz, vec2(800.));

    bp += rand(gr.xyy) * 200.;

    box = s;
    box.color = vec4[](
        vec4(1., 0., 0., 0.),
        vec4(0., 0.8, 1., 0.)
    )[int(floor(rand(gr.xyx) + .5))];

    box.diffuse = 1.;
    box.specular = 0.0;
    box.materialIndex = 1.;
    box.dist = fBox(bp, vec3(20., 3320., 20.));
    box.glow = 1.;

    s = geoU(s, box);

    return s;
}

// 	standard sphere tracing inside and outside
geometry trace(vec3 ro,vec3 rd)
{
    geometry mp = map(ro);
    mp.glow = 0.;
    float minDist = INFINITY;
    float function_sign=(mp.dist<0.)?-1.:1.;
    float precis=.001;
    float h=precis*1.;
    float t=-0.0;

	for(int i=0;i<170;i++)
	{
        if(abs(h)<precis||t>FAR)break;
		mp = map(ro+rd*t);
        mp.i = float(i);
        h=function_sign*mp.dist;
        t+=h * .4;
	}
    mp.dist = t;
    return mp;
}

vec4 clouds(vec3 rd, vec3 ro) {
    vec2 uv = rd.xz / rd.y * .4;

    vec4 clouds = vec4(
        SmoothNoise(rd * 3.) * vec4(0., 0., 1., 1.) +
        SmoothNoise(rd) * vec4(1., 1., 0., 1.)


    ) - rd.y;
 	return clouds * max(1., rd.y) * 1.;
}

float SphereIntersect( in vec3 ro, in vec3 rd, in vec4 sph )
{
	vec3 oc = ro - sph.xyz;
	float b = dot( oc, rd );
	float c = dot( oc, oc ) - sph.w*sph.w;
	float h = b*b - c;
	if( h<0.0 ) return -1.0;
	return -b - sqrt( h );
}

vec4 Sky(in vec3 rd, bool showSun, vec3 lightDir, vec3 ro)
{

    float sunSize = 0.;
    float sunAmount = max(dot(rd, lightDir), 1.);
    float v = pow(1. - max(rd.y, 0.0), 12.);
    vec4 cl = mix(vec4(1., 0., 0., 1.), vec4(0., 0., 1., 1.), rd.y);
    vec4 sky = mix(cl, vec4(.0, .0, 0., 1.), v);
 	vec3 lightColour = vec3(.1, .2, .3);

    sky += cl * sunAmount * sunAmount + vec4(1., 1., 0., 1.) *  min(pow(sunAmount, 120.0) * sunSize, 1. * sunSize);

    vec3 moon = vec3(0., 0., 22000. + iTime * 320.);

    float planetDist = SphereIntersect(ro, rd, vec4(moon, 17000.));

    sky += clouds(rd, ro);


    vec3 moo = ro + planetDist * rd;
    vec3 nor = normalize(moo-moon);

    if (planetDist > 0.) {
        vec3 nr = nor;
        vec3 lp = normalize(light);

		pR(nr.xy, .3);
        pR(nr.xz, iTime * .1);

		pR(nr.xz, n(nr * 10. - nr.yxz * 3.) * 2.);
        moo = mix(vec3(1., 1., 1.), vec3(1., .5, 0.), n(nr * vec3(.2, 9., .5) * 11.));

        sky.rgb = moo * pow(abs(dot(rd, lp)), 3.);
    } else {
		sky += clouds(rd, ro);
	}

    sky += clouds(rd, ro);

    return clamp(sky, 0.0, 1.0) ;
}

vec4 doColor( in vec3 sp, in vec3 rd, in vec3 sn, in vec3 lp, geometry obj) {
	vec3 ld = lp;
    float lDist = max(length(ld / 2.), 0.01);
    ld /= lDist;

    float diff = max(dot(sn, ld), obj.diffuse);
    float spec = pow(max(dot(reflect(ld, sn), rd), 1.), obj.specular);

    vec4 objCol = obj.color;

    return objCol * (diff) + spec * .001;
}

//	normal calculation
vec3 normal(vec3 pos)
{
    float eps=0.01;
	float d=map(pos).dist;
	return normalize(vec3(map(pos+vec3(eps,0,0)).dist-d,map(pos+vec3(0,eps,0)).dist-d,map(pos+vec3(0,0,eps)).dist-d));
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {

    float
        mat = 0.,
        camShY = 0.;

    vec2 uv = fragCoord.xy / iResolution.xy - .5;
    uv.x *= iResolution.x/iResolution.y;

    uv *= 2.4;
    light = vec3(20.,40., 132.);

    vec3
        vuv = vec3(0., 1., 0. );

    ro = vec3(0., -50., 0. + iTime * 320.);

    vec3 vrp =  vec3(0., 0., 120.) + ro;
    vec3 vpn = normalize(vrp - ro),
    	u = normalize(cross(vuv, vpn)),
    	rd = normalize(vec3(uv * 2., 1.)),
        hit;

    vec3 oro = ro;

    vec4 sceneColor = vec4(0., 0., 0., 0.);

    geometry tr = trace(ro, rd);
    geometry otr = tr;

    hit = ro + rd * tr.dist;

    vec3 ohit = hit;
    float odist = tr.dist;
    vec3 sn = normal(hit);

	vec4 sky = Sky(rd, true, normalize(light), ro) * 1.;
    vec4 osky = sky;
    float glow = 0.;

    if (tr.dist < FAR) {
        sceneColor = (doColor(hit, rd, sn, light, tr) * 1.) * 1.;
	    glow = tr.glow;

        if (tr.materialIndex == 0.) {
            sceneColor = mix(
                vec4(1., 1., 1., 1.),
                sceneColor,
                smoothstep(.0, .075, abs(fract(ohit.x * .01) * 2. - 1.)) *
                smoothstep(.0, .075, abs(fract(ohit.z * .003) * 2. - 1.))
            );
            sceneColor += length(cross(sn, normal(sn.zyx + 1.5)) * .5);
        }

        sceneColor.a = pow(abs(sceneColor.a - .5) * 1.2, 4.) * 9.;

        if (tr.materialIndex == 0.) {
            sceneColor *= 1.-max(0., dot(sn, -light)) * .01;
			rd = reflect(-rd, sn);
            sceneColor += Sky(rd, true, normalize(light), ro) * .1 * sn.y;
        }

        sceneColor = mix(sceneColor, sky, otr.dist / FAR/ 4.);
    } else {
        sceneColor = sky;
    }

    fragColor = vec4(clamp(sceneColor * (1. - length(uv) / 3.5), 0.0, 1.0));
    fragColor.a = glow;
    fragColor.rgb = pow(fragColor.rgb, vec3(1.5));
}
