
#define AA 1.
#define distThresh .001

mat4 rotationMatrix(vec3 axis, float angle)
{
    axis = normalize(axis);
    float s = sin(angle);
    float c = cos(angle);
    float oc = 1.0 - c;

    return mat4(oc * axis.x * axis.x + c,           oc * axis.x * axis.y - axis.z * s,  oc * axis.z * axis.x + axis.y * s,  0.0,
                oc * axis.x * axis.y + axis.z * s,  oc * axis.y * axis.y + c,           oc * axis.y * axis.z - axis.x * s,  0.0,
                oc * axis.z * axis.x - axis.y * s,  oc * axis.y * axis.z + axis.x * s,  oc * axis.z * axis.z + c,           0.0,
                0.0,                                0.0,                                0.0,                                1.0);
}

vec3 rotate(vec3 vector, vec3 axis, float angle){
    return (vec4(vector, 1.)*rotationMatrix(axis, angle)).xyz;
}

float DistToLine(vec3 pt1, vec3 pt2, vec3 testPt)
{
    return length(cross(pt1-pt2, pt2-testPt));
}

float color(float offset){
    return min(.5,max(0.,abs(-(abs(mod(offset,1.))-.5))*2.-.3333))*2.;
}

float getDistance(vec3 p, float distMul, float bigR, float smallR){
 	return length(vec2(length(p.xz)*distMul-bigR, p.y))-smallR;
}
vec3 raymarch(int steps,
              vec3 ro,
              vec3 rd,
              float bigR,
              float smallR,
              out float distMul,
              out float distOrigin,
              out float distSurface,
              out vec2 tex,
              out float minDist,
              out float minLocked){
    vec3 p=ro;
    tex;
    distOrigin=0.;
    for(int i=0; i<steps; i++){

        tex.x = atan(p.x, p.z);
        tex.y = atan(p.y, (length(p.xz)-bigR)/smallR);
        distMul = sin(iTime)*sin(tex.x*15.)*sin(tex.y*10.)*.2+1.;

        //distMul=sin(a*6.)*sin(iTime)*.1+.8;
        distSurface = getDistance(p, distMul, bigR, smallR);
        if(distSurface<minDist) minDist=distSurface;
        else minLocked = minDist+.04*minDist/distSurface;
        p+=rd*distSurface*.5;
        distOrigin+=distSurface*.5;
        if(distOrigin>15. || distSurface<distThresh){
            break;
        }
    }
	return p;
}


void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 mouz = iMouse.xy;
    if(mouz.x<.01){
        mouz.x=100.;
        mouz.y=100.;
    }
    float zoom = (mouz.x+10.)/iResolution.x;
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;
    uv-=.5;
    uv.x *= iResolution.x/iResolution.y;
    vec3 col = vec3(0.);
    vec2 uvAA = uv;
    float lightColorSpeed = .1;
    vec3  cam = vec3(0., -((mouz.y+.01)/iResolution.y*10.-5.), 3.),
        //light = vec3(sin(iTime)*3., abs(sin(iTime*5.))*1., cos(iTime)*3.),
        //lightColor = vec3(color(iTime*lightColorSpeed), color(iTime*lightColorSpeed+.3333), color(iTime*lightColorSpeed+.6666)),
        lightDir, normal,
        lookat = vec3(0.),
        fwd = normalize(lookat-cam),
        r = normalize(cross(fwd, vec3(0.,1.,0.))),
        u = cross(fwd, r),
        c = cam + fwd*zoom;

    vec3[2] lights;
    lights[0] = vec3(sin(iTime)*3., abs(sin(iTime*5.))*1., cos(iTime)*3.);
    lights[1] = vec3(1.);
    vec3[2] lightColors;
    lightColors[0]=vec3(color(iTime*lightColorSpeed), color(iTime*lightColorSpeed+.3333), color(iTime*lightColorSpeed+.6666));
    lightColors[1]=.5*vec3(color(iTime*lightColorSpeed+.5), color(iTime*lightColorSpeed+.3333+.5), color(iTime*lightColorSpeed+.6666+.5));

    float bigR = sin(iTime)*.1+1.1;
    float smallR = -cos(iTime)*.2+.5;
    vec2 tex;

    for(float aaX=0.; aaX<AA;aaX++){
    	uvAA.y = uv.y;
        uvAA.x+=1./(AA*iResolution.y);
        for(float aaY=0.; aaY<AA;aaY++){
            uvAA.y+=1./(AA*iResolution.y);
            vec3 i = c+r*uvAA.x-u*uvAA.y,
                ray = normalize(i-cam);
                //ray = rotate(rotate(fwd, r, uvAA.y/zoom),u, uvAA.x/zoom);


            float distSur, distOrigin=0., minDist=100., minLocked=100., distMul;
            vec3 p = cam;
            /*for(int i=0; i<1000; i++){
                float a=atan(p.x,p.z);
                distMul=sin(a*6.)*sin(iTime)*.1+.8;
                distSur = getDistance(p, distMul, bigR, smallR);
                if(distSur<0.0001||distOrigin>15.) break;

                if(distSur<minDist) minDist=distSur;
                else minLocked = minDist+.04*minDist/distSur;

                p+=ray * distSur;
                distOrigin+=distSur;
            }*/
            p=raymarch(1000, p, ray, bigR, smallR, distMul, distOrigin, distSur, tex, minDist, minLocked);

            for(int l=0; l<lights.length(); l++){
                vec3 light = lights[l];
                vec3 lightColor = vec3(.5)+lightColors[l];

                if(distSur<distThresh){
                    col+=0.01;
                    lightDir = -normalize(p-light);

					//normal = normalize(p-bigR*normalize(vec3(p.x,0.,p.z)));
                    float epsilon = 0.001; // arbitrary — should be smaller than any surface detail in your distance function, but not so small as to get lost in float precision
                    float centerDistance = getDistance(p, distMul, bigR, smallR);
                    float xDistance = getDistance(p + vec3(epsilon, 0, 0), distMul, bigR, smallR);
                    float yDistance = getDistance(p + vec3(0, epsilon, 0), distMul, bigR, smallR);
                    float zDistance = getDistance(p + vec3(0, 0, epsilon), distMul, bigR, smallR);
                    normal = normalize((vec3(xDistance, yDistance, zDistance) - centerDistance) / epsilon);

                    //tex.x = atan(p.x,p.z);
                    //tex.y = length(p.xz)*distMul-bigR;
                    float omit;
                    vec2 omit2;
                    float shadow = length(raymarch(20, light, normalize(p-light), bigR, smallR, omit, omit, omit, omit2, omit, omit)-p);
                    float lightInt = 2./length(p-light) - shadow;

                    // diffuse
                    col+=lightColor*(clamp(dot(normal, lightDir),0.,1.)*lightInt);
                    col.gb*=1.-.1*abs(sin(tex.x*10.+tex.y*10.));
                    //col.r+=.1*(1.-abs(sin(tex.x*10.+tex.y*10.)));

                    //specular
                    float sharpness=pow(2.,6.);
                    float specInt = 5.;
                    col+=lightColor*specInt*(.6*pow(max(0.,dot(-ray, reflect(-lightDir, normal))),sharpness)*lightInt);
                }
                else{
                    // light dots
                    col+=lightColor*pow(.1/DistToLine(cam, cam+ray, light),1.1);
                }
            }
            // glow
            col+=sin(iTime*2.3)*1.*clamp(lightColors[0].yzx*pow(.01/minLocked,.7),0.,1.);
        }
    }

    col/=AA*AA;
    // Output to screen
    fragColor = vec4(col,1.0);
}