# define f 1

const float END = 20.;
const float ep = 0.001;

mat2 rot(float a){
	return mat2(cos(a), -sin(a), sin(a), cos(a));
}

float cube(vec3 p, float b, float r){
    vec3 d = abs(p) - b;
    return length(max(d,0.0)) - r + min(max(d.x,max(d.y,d.z)),0.0);
}

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

float plane( vec3 p, vec4 n )
{
  	return dot(p,n.xyz) + n.w;
}

float sdEllipsoid( vec3 p, vec3 r )
{
  float k0 = length(p/r);
  float k1 = length(p/(r*r));
  return k0*(k0-1.0)/k1;
}

float light(vec3 p){
    vec3 move = vec3(0., sin(iTime), 0.);
    vec3 off = vec3(0., -1., 0.);
    vec3 p_ = p - move + off; p_.xy *= rot(iTime * 3.); p_.zy *= rot(iTime * 2.);
	return min(cube(p_, .17, 0.), sphere(p + move + off, .2)); 
}

# define ang 6.2831853 / 5.
# define frames 9

struct PETAL {
    float curve1;
    float curve2;
    float curve3;
    float curve4;
    float curve5;
    float wide;
    float bound;
    float opt;
};

PETAL ptlayers[frames] = PETAL[frames](PETAL(1., .9, 1.0, 0., 0., 1.5, 3.5, .6),
                                       PETAL(1., .9, 1.0, .1, .2, 1.3, 3.5, .6),
                                       PETAL(1.7, .9, 0.8, .1, .2, 1.0, 3.5, .4),
                                       PETAL(2.3, .9, 0.5, .1, .3, 1.0, 3.5, .4),
                                       PETAL(3., .9, 0.5, .5, .3, 1.0, 3.5, .4),
                                       PETAL(3., .9, 0.5, .5, .3, 1.0, 3.5, .4),
                                       PETAL(3., .9, 0.5, .5, .5, 1.0, 3.5, .4),
                                       PETAL(3., .9, 0.5, .5, .7, 1.0, 3.5, .4),
                                       PETAL(3., .9, 0.5, .5, .9, 1.0, 3.5, .4));
float ptoffsetx[frames] = float[frames](3., 2.5, 1.8, 1.6, 1.6, 1.6, 1.5, 1.3, 1.);
float ptoffsety[frames] = float[frames](.0, .0, .2, .2, .0, .0, .0, .0, .0);

float sdPetal(vec3 P, PETAL pt)
{
    
    vec3 p, p0;
    
    P.xy *= rot(-pt.curve5);
	p = P;
    
    // Contain the heavy distortion to speed up raytracing
    float bound = length(p) - pt.bound;
    float d = bound + 1.;
    if (bound < 0.)
    {
        p0 = p;
        p.y = p0.y - sin(p0.x*pt.curve3) * pt.curve1 + cos(p0.z)*pt.curve2;
        p.xy *= rot(-p0.y * pt.curve4);
        float petal = sdEllipsoid(p, vec3(2., 0.3, 1.5*pt.wide)) * pt.opt;
        
        d = petal;
    }
    
    return d;
}


float sdPetals(vec3 P)
{
    
    vec3 p;
    float d;

    p = P;

    // Repeat radially
    float a = mod(atan(p.z,p.x), ang) - ang/2.;
    p.xz = vec2(length(p.xz), 0.) * rot(a);

    p.x -= ptoffsetx[f];
    p.y -= ptoffsety[f];
    d = sdPetal(p, ptlayers[f]);
    
    return d;
}

float obj(vec3 P)
{
    vec3 p;
    float d;
    
    p = P*2.;
    float Petals = sdPetals(p);
    
    p = P;
    p.y += 1.7;
    float base = sdEllipsoid(p, 1.4*vec3(1., .5, 1.));
    
    d = min(Petals, base)/2.;
    
    return d;
}

float mirror(vec3 p){
    float ripples = 0.1*sin(3.*length(p.xz) - iTime*pow(abs(sin(iTime*0.1)*0.5), 4.));
    //return plane(p, vec4(0.,1.,0., 1.1)) + ripples;
    return cube(p + vec3(0., 4., 0.), 1.7, 0.3) + ripples;
}

float SDscene(vec3 p){
   
	float obj = obj(p);
    float mirror = mirror(p);
    float light = light(p);
    float d = min(min(obj, mirror), light);
    
    return d;
}

vec3 SDnormal(vec3 p){
    
    //Calculates the normal vector of SDscene
    
    return normalize(vec3(
    SDscene(vec3(p.x+ep,p.y,p.z))-SDscene(vec3(p.x-ep,p.y,p.z)),
    SDscene(vec3(p.x,p.y+ep,p.z))-SDscene(vec3(p.x,p.y-ep,p.z)),
    SDscene(vec3(p.x,p.y,p.z+ep))-SDscene(vec3(p.x,p.y,p.z-ep))
    ));
}

float depth(vec3 ro, vec3 rd, float sig, inout float min_l){
    
    //Returns depth from ro given raydirection
    
    int max=300;
    vec3 p;
    
    float dist=0., d;
    for (int i=0; i<max; i++){
        p = ro + dist*rd;
    	d = SDscene(p)*sig;
        if (light(p) < min_l){ min_l = light(p);}
    if (abs(d)<ep){
        return dist;
    }
    dist += d;
    if (dist > END){
        return END;
    }
  }
}

void ray_mirror(inout vec3 ro, inout vec3 rd, inout float d, inout float min_l){
    
    int Nmax = 15, count = 0;
    while (count < Nmax){
        
        ro -= rd*ep*5.;
        rd = normalize(reflect(rd, SDnormal(ro)));
        d = depth(ro, rd, 1., min_l);
        ro += d*rd;
       
        if (mirror(ro) > ep){break;}
        
        count += 1;
    }
}

void ray_obj(inout vec3 ro, inout vec3 rd, inout float Dglass, inout float d, inout float min_l){
    
    int Nmax = 15, count = 0, count2 = 0;
    vec3 p, rd_;
    while (count < Nmax){
        
        //Go into glass
        ro += rd * ep*50.;
        rd = normalize(refract(rd, SDnormal(ro), 0.6));
        d = depth(ro, rd, -1., min_l);
        ro += rd * d;
    	Dglass += d;
        
        //internal refraction
    	rd_ = refract(rd, -SDnormal(ro), 1.5);
       	while (length(rd_) < 0.0001 && count2 < Nmax){
            
            rd = normalize(reflect(rd, -SDnormal(ro)));
            d = depth(ro, rd, -1., min_l);
            ro += d*rd;
            
            Dglass += d;
            rd_ = refract(rd, -SDnormal(ro), 1.5);
            count2 += 1;
        }
  
        if (length(rd_) > 0.0001){rd = normalize(rd_);}
        ro += rd * ep*10.;
        d = depth(ro, rd, 1., min_l);
        ro += rd * d;
        
        if (obj(ro) > ep){break;}
        
        //if (mirror(ro) > ep){ break;}
      	
		count += 1;
    }
}


void fresnel(vec3 ro, vec3 rd, inout float refl, inout float refr){
 	   
   	float b = ((1. - 1.5)/(1. + 1.5));
    float r0 = b*b;
    refl = r0 + (1. - r0)*pow((1. - abs(dot(SDnormal(ro), normalize(rd)))), 5.);
    refr = 1.-refl;
    //refl = .5; refr = .5;
}

vec3 render(vec2 uv){
    vec3 col;
    
    //Camera
    float ScreenSize = 4.;
    float shake = .7*sin(.3*iTime);
    
    float zoom = 2.5;
    float k = 0.4;
    float osc = sin(iTime*.3); //3.5 + 2.*osc*osc
  	vec3 ro = 6.*vec3(sin(k*iTime), shake, cos(k*iTime)) + vec3(0.,2.,0.);
  	vec3 lookat = vec3(0,0,0);
    
    
  	vec3 fw = normalize(lookat - ro);
  	vec3 r = normalize(cross(vec3(0,1.,0), fw));
  	vec3 up = normalize(cross(fw,r));
  	vec3 scrC = ro + (zoom)*fw;
  	vec3 scrP = scrC + (uv.x*r + uv.y*up) * ScreenSize;
  	vec3 rd = normalize(scrP - ro);
    
    float Dglass, min_l = END;
    float d = depth(ro, rd, 1., min_l);
    ro += d*rd;
    
    vec3 ro_, rd_;
    float refl, refr;
    int Nmax = 15, count;
    while (count < Nmax){
        //hits background
        if (d > END - ep){
            col += texture(iChannel0, ro).xyz;
            vec3 tint = vec3(exp(Dglass*-0.05),exp(Dglass*-0.3),exp(Dglass*-0.7));
            col *= tint;
            col += pow(clamp(abs(1./min_l)*0.1, 0., 1.), .7);
            break;
        }
        
        //hit light
        else if (light(ro) < ep){
        	col += vec3(1.);
            break;
        }

        //hit obj
        else if (obj(ro) < ep){
            ro_ = ro; rd_ = rd;
            ray_obj(ro, rd, Dglass, d, min_l);
            ray_mirror(ro_, rd_, d, min_l);
        }

        //hit mirror
        else if (mirror(ro) < ep){
            ray_mirror(ro, rd, d, min_l);
        }
        
        else{d = END;}
        
        count += 1;
    }
    return col;
}



void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    
    float degree = .35*pow(0.538502*(sin(3.*iTime) + sin(iTime * 1.8)),8.) + .1;
    
    //Shader setup
    vec2 R = iResolution.xy;
    vec2 uv = (fragCoord - 0.5*R)/R.x;
    vec3 col = render(uv);
    fragColor = vec4(col ,1.);
    
}
