#define MAX_STEPS 70
#define MAX_DIST 50.
#define SURF_HIT 0.005
#define EPSILON 0.001


const float PI = 3.1415926535897932384626433832795;

// Rotation matrix around the Y axis.
mat3 rotateY(float theta) {
    float c = cos(theta);
    float s = sin(theta);
    return mat3(
        vec3(c, 0, s),
        vec3(0, 1, 0),
        vec3(-s, 0, c)
    );
}

// Rotation matrix around the Z axis.
mat3 rotateZ(float theta) {
    float c = cos(theta);
    float s = sin(theta);
    return mat3(
        vec3(c, -s, 0),
        vec3(s, c, 0),
        vec3(0, 0, 1)
    );
}


float sphere(vec3 p, float radius) {
    return length(p) - radius;
}


float torus( vec3 p, vec2 t ) {
  vec2 q = vec2(length(p.xz)-t.x,p.y);
  return length(q)-t.y;
}


vec3 map_texture(vec3 p) {
    return texture(iChannel0, p.xz * .5 + iTime * .05).xyz * .01;
}


float dist_field(vec3 p) {
    float c = 0.5;
    return torus(mod(
        c * .5 + iTime * .1 + .5 + vec3(p.x + .2, p.y - .3, p.z + iTime * .0001)
        * rotateY(p.y * 0.02)
        * rotateZ(p.z * .3) - .5 * c, c)
        - map_texture(p)
        - c * .5,
        vec2(0.2, abs(cos(iTime*6.+p.x*1.*p.z*10.) * 0.004 + .06)));
}


float ray_march(vec3 ro, vec3 rd) {
    float dist = 0.0f;

    for (int i = 0; i < MAX_STEPS; i++) {
        vec3 p = ro + rd * dist;
        float _dist = dist_field(p);

        dist += _dist;

        if (_dist < SURF_HIT || dist > MAX_DIST) break;
    }

    return dist;
}


vec3 get_normal(vec3 p) {
    vec2 eps = vec2(EPSILON, 0.);
    float d = dist_field(p);
    vec3 n = vec3(
        d - dist_field(p - eps.xyy),
        d - dist_field(p - eps.yxy),
        d - dist_field(p - eps.yyx)
    );

    return normalize(n);
}


float get_light(vec3 light_pos, vec3 p, float spec_pow) {
    vec3 l = normalize(light_pos - p);
    vec3 n = get_normal(p);
    float diffuse = max(dot(n, l), 0.);
    float specular = pow(max(dot(l, reflect(-l, n)), 0.), spec_pow);

    float d = ray_march(p + n * 2. * SURF_HIT, l);
    if (d < length(light_pos - p)) {
        diffuse *= .1;
        specular = .1;
    }

    return diffuse + specular;
}


void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    vec3 eye = vec3(.1, .6,-0.3);
    vec3 eye_dir = vec3(uv, cos(iTime * 0.1));

    float d = ray_march(eye, eye_dir);
    vec3 p = eye + eye_dir * d;
    vec3 sky = vec3(
           .2 * p.x,
           .4 * p.y,
           .5
    );
    vec3 col = sky * 1.;

    vec3 light_pos = eye - .2;
    vec3 light_pos1 = eye + .2;
    float light = get_light(light_pos, p, 64.);
    float light1 = get_light(light_pos1, p, 32.);

    if (d <= MAX_DIST) {
        col *= light + light1;
    } else {
        col = sky;
    }

    col = pow(col, vec3(.4545));

    fragColor = vec4(col, 1.0);
}