// fork of https://www.shadertoy.com/view/4tGGRV by zackpudil
// zackpudil deserves all the credit for how cool the lighting looks. 
// I replaced the mandelbox with dodecahedron gasket and made some other changes.
// philip.bertani@gmail.com

int max_iter = 5;  //try increasing if you have a  nice GPU
float ifs_scale = 1.75;
vec3 ro;


//hard coding the vertices for a dodecahedron
const float gmh=(sqrt(5.)+1.)/2.;
const float gmi=1./gmh;

vec3[] d12 = vec3[]  (
vec3(1.,1.,1.),
vec3(1.,1.,-1.),
vec3(1.,-1,1.),
vec3(1.,-1,-1.),
vec3(-1,1.,1.),
vec3(-1.,1.,-1),
vec3(-1.,-1.,1.),
vec3(-1.,-1.,-1.),
vec3(0.,gmh,gmi),
vec3(0.,gmh,-gmi),
vec3(0.,-gmh,gmi),
vec3(0.,-gmh,-gmi),
vec3(gmi,0.,gmh),
vec3(gmi,0.,-gmh),
vec3(-gmi,0.,gmh),
vec3(-gmi,0.,-gmh),
vec3(gmh,gmi,0.),
vec3(gmh,-gmi,0.),
vec3(-gmh,gmi,0.),
vec3(-gmh,-gmi,0.)
);


float hash(float n) {
    return fract(sin(n)*50000.);
}


mat3 rotx(float an) {
    float cc = cos(an), ss=sin(an);
    return mat3(1,0.,0.,0.,cc,-ss,0.,ss,cc);

}

mat3 rot(float an) {
    float cc = cos(an), ss=sin(an);
    return mat3(cc,0.,-ss,0.,1.,0.,ss,0.,cc);

}


float de(vec3 z) {
    
    vec3 min_vtx;
    vec3 orig_z = z;
    int n=0;
    float min_dist,dist_to_vtx;


    for (int i=0; i<100; i++) {
       
        if ( i > max_iter ) break;

        float sc = 4.;
        float w = iTime*1.5;
        vec3 dd_0 = rot(w)*d12[0]*sc;
        min_vtx = dd_0;
        min_dist=length(z-dd_0);
        for (int j=1; j<20; j++) {
            vec3 ddj = rot(w)*d12[j]*sc;
            dist_to_vtx=length(z-ddj); 
            if (dist_to_vtx<min_dist) {min_vtx=ddj; min_dist=dist_to_vtx;}
            
        }
        
        z = min_vtx + ifs_scale*(z-min_vtx);
        
        n++;
        
    }

    float dz = pow(ifs_scale, float(n) );
    
    float f = (length(z)-.9)/dz;
    
    f = max( f, -(length(orig_z-ro)-2.) );
    
    return f;
}

float trace(vec3 ro, vec3 rd, float mx) {
    float t = 0.0;
    for(int i = 0; i < 80; i++) {
        float d = de(ro + rd*t);
        if(d < 0.001 || t >= mx) break;
        t += d;
    }
    
    if(t < mx) return t;
    return -1.0;
}

// Improvement thanks to Shane. vstrace= shadow trace in volumentric loop.
// less detailed, dithering and breaks quicker.
float vstrace(vec3 ro, vec3 rd, float mx) {
    float t = 0.1*hash(dot(ro, rd));
    for(int i = 0; i < 50; i++) {
        float d = de(ro + rd*t);
        if(d < 0.01 || t >= mx) break;
        t += d;
    }
    
    if(t < mx) return t;
    return -1.0;
}

vec3 normal(vec3 p, out float e) {
    vec2 h = vec2(0.001, 0.0);
    
    vec3 n1 = vec3(
        de(p + h.xyy),
        de(p + h.yxy),
        de(p + h.yyx)
	);
    
    vec3 n2 = vec3(
        de(p - h.xyy),
        de(p - h.yxy),
        de(p - h.yyx)
	);
    
    return normalize(n1 - n2);
}


void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 uv = (-iResolution.xy + 2.0*fragCoord)/iResolution.y;
    
    
    ro = vec3(0.,0.,2.);
    vec3 rd = normalize( vec3(uv, -3.));
    
    vec3 col = vec3(.3);
    
    float t = trace(ro, rd, 30.0);
    if(t > 0.0) {
        float edg;
        vec3 pos = ro + rd*t;
        vec3 nor = normal(pos, edg);
        
        float occ = 1.0;
        
        vec3 lig = normalize(-pos);
        float dis = length(pos);
        
        // direct lighting with hard shadows.
        col += 0.3*clamp(dot(lig, nor), 0.0, 1.0)
            *step(0.0, -trace(pos + nor*0.001, lig, dis));
        
        // indirect lighting with ambient occlusion.
        col += 0.3*clamp(dot(-lig, nor), 0.0, 1.0)*occ;
        
        col *= pos*exp(-t/2.);

    }
    
    // volumetric shadows
    float s = hash(dot(uv, vec2(12.23, 39.343)))*0.05;
    float vol = 0.0;
    // need less light strength the closer you are to the light.
    float e = 0.35*smoothstep(0.0, 3., length(ro));
    for(int i = 0; i < 90; i++) {
        if(s > t) break;
        vec3 pos = ro + rd*s;
        
        vec3 lig = normalize(-pos+vec3(0.,0.,-1.5+2.*sin(iTime/2.)));
        float dis = length( pos);
        
        // shadow trace at each position along the march.
        float l = step(0.0, -vstrace(pos, lig, dis));
        // light strength is proportional to distance from light.
        l *= e/dis;
        
        vol += l;
        s += 0.1;
    }
    
    // blue light rays.
    float cx = 1.+max(0.,sin(iTime*2.));
    col += .1*vec3(0.4*cx*vol, 0.3*cx*vol, vol/cx);
    

	fragColor = vec4(col, 1);
}
