#define fdist 0.5
#define iters 90
#define tol 0.001
#define maxdist 100.
#define eps 0.01
#define tmin 0.1
#define bevel 0.1
#define innerradius 0.2
#define sidelength 0.3
#define gridlength 1.0

#define PI 3.1415926

float box(vec3 ro, vec3 rd) {
    ro = mod(ro, gridlength) - gridlength/2.;
    vec3 t = -ro/rd + abs(gridlength/rd/2.);
    return min(t.x, min(t.y, t.z));
}

vec2 map(vec3 ro) {
    ro = mod(ro, gridlength) - gridlength/2.;
    
    //beveled cube
    vec3 rad = clamp(ro, -sidelength, sidelength);
    float d = length(ro-rad) - bevel;
    
    d = max(d, -length(ro.xy-clamp(ro.xy, -innerradius, innerradius))+bevel);
    d = max(d, -length(ro.yz-clamp(ro.yz, -innerradius, innerradius))+bevel);
    d = max(d, -length(ro.zx-clamp(ro.zx, -innerradius, innerradius))+bevel);
    
    return vec2(d, 1.0);
}

vec2 map(vec3 ro, vec3 rd) {
    float t = box(ro, rd);
    vec2 m = map(ro);
    if (m.x < t) {
        return m;
    }
    return vec2(t+tol, 0.0);
}

vec3 raytrace(vec3 ro, vec3 rd) {
    float t = 0.;
    vec2 m = map(ro, rd);
    //fac.xyz are consecutive measurements, fac.w is the last local minimum
    vec4 fac = vec4(maxdist);
    float lastmat = 0.;
    int i;
    for (i=0; i<iters; i++) {
        t += m.x;
        m = map(ro + rd * t, rd);
        fac.x = m.x;// / t;
        //update facmin to last seen local minimum (not counting voxel borders)
        if (lastmat > 0. && fac.y < fac.x && fac.y <= fac.z && fac.y < fac.w) {
            fac.w = fac.y;
        }
        lastmat = m.y;
        fac.zy = fac.yx;
        if (abs(m.x) < tol) {
            return vec3(t, m.y, fac.w);
        } else if (m.x > maxdist) {
            break;
        }
    }
    return vec3(t, 0., fac.w);
}

vec4 getnormal(vec3 ro) {
    vec2 d = vec2(eps, 0.0);
    float x1 = map(ro+d.xyy).x;
    float x2 = map(ro-d.xyy).x;
    float y1 = map(ro+d.yxy).x;
    float y2 = map(ro-d.yxy).x;
    float z1 = map(ro+d.yyx).x;
    float z2 = map(ro-d.yyx).x;
    return vec4(normalize(vec3(
        x1-x2,
        y1-y2,
        z1-z2)),
        x1+x2+y1+y2+z1+z2-6.*map(ro).x);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    float h = iMouse.y < 1. ? 0. : (iMouse.y-iResolution.y/2.)/200.;
    float ww = iMouse.x < 1. ? 0. : (iMouse.x-iResolution.x/2.)/200.;
    vec3 ro = vec3(iTime/2., 0.5 + sin(iTime/3.)/5., 0.5 + cos(iTime/3.)/5.);
    vec3 w = vec3(cos(h)*cos(ww), sin(h)*cos(ww), sin(ww));
    vec3 u = normalize(cross(w, vec3(0., 10.0, 0.)));
    vec3 v = cross(u, w);
    vec3 rd = normalize(w*fdist+(fragCoord.x/iResolution.x-0.5)*u+(fragCoord.y-iResolution.y/2.0)/iResolution.x*v);
    vec3 d = raytrace(ro, rd);
    vec4 n = getnormal(ro + rd*d.x);
    fragColor = d.y < 0.5 ? vec4(1.) : vec4(vec3(smoothstep(0.01, 0.005, d.z)) + n.xyz/4.-0.5 + smoothstep(0.02, 0.01, n.w),1.0);
}
