﻿precision highp float;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform sampler2D backbuffer;

// ------------------------------------------------------------------------------------
// Original Character By MikkaBouzu : https://twitter.com/mikkabouzu777
// ------------------------------------------------------------------------------------

#define saturate(x) clamp(x, 0.0, 1.0)
#define MAX_MARCH 256
#define MAX_DIST 100.

const float EPS = 1e-3;
const float EPS_N = 1e-4;
const float OFFSET = EPS * 10.0;

#define M_PI 3.1415926
#define M_PI2 6.2831852
#define RAD90 (M_PI * 0.5)

struct surface {
	float dist;
    vec4 albedo;
    int count;
    bool isHit;
};

// Surface Data Define
#define SURF_NOHIT(d)   (surface(d, vec4(0), 		 	0, false))
#define SURF_BLACK(d) 	(surface(d, vec4(0,0,0,1),   	0, true))
#define SURF_FACE(d) 	(surface(d, vec4(1,0.7,0.6,1), 	0, true))
#define SURF_MOUSE(d) 	(surface(d, vec4(1,0,0.1,1),   	0, true))
#define SURF_CHEEP(d) 	(surface(d, vec4(1,0.3,0.4,1), 	0, true))

vec3 hsv2rgb( in vec3 c )
{
    vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );

	rgb = rgb*rgb*(3.0-2.0*rgb); // cubic smoothing	

	return c.z * mix( vec3(1.0), rgb, c.y);
}

#define M_PI03 1.04719
#define M_PI06 2.09439
vec3 sinebow(float h) {
    vec3 r = sin((.5-h)*M_PI + vec3(0,M_PI03,M_PI06));
    return r*r;
}

//  1 out, 3 in...
float hash13(vec3 p3)
{
	p3  = fract(p3 * .1031);
    p3 += dot(p3, p3.zyx + 31.32);
    return fract((p3.x + p3.y) * p3.z);
}

float noise(vec3 st)
{
	vec3 ip = floor(st);
  vec3 fp = smoothstep(vec3(0.), vec3(1.), fract(st));
    
	vec4 a = vec4(hash13(ip+vec3(0.)),hash13(ip+vec3(1.,0.,0.)),hash13(ip+vec3(0.,1.,0.)),hash13(ip+vec3(1.,1.,0.)));
  vec4 b = vec4(hash13(ip+vec3(0.,0.,1.)),hash13(ip+vec3(1.,0.,1.)),hash13(ip+vec3(0.,1.,1.)),hash13(ip+vec3(1.,1.,1.)));
    
	a = mix(a, b, fp.z);
	a.xy = mix(a.xy, a.zw, fp.y);
    
	return mix(a.x, a.y, fp.x);
}

mat3 m = mat3( 0.00,  0.80,  0.60,
              -0.80,  0.36, -0.48,
              -0.60, -0.48,  0.64 );

float fbm( vec3 p )
{
    float f;
    f  = 0.5000*noise( p ); p = m*p*2.02;
    f += 0.2500*noise( p ); p = m*p*2.03;
    f += 0.1250*noise( p );
    return f;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Basic Distance function
/////////////////////////////////////////////////////////////////////////////////////////////////
float sdRoundBox(vec3 p, vec3 size, float r)
{
    return length(max(abs(p) - size * 0.5, 0.0)) - r;
}

float sdCapsule(vec3 p, vec3 a, vec3 b, float r)
{
	vec3 pa = p - a, ba = b - a;
	float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
	return length(pa - ba*h) - r;
}

float sdEllipsoid( vec3 p, vec3 r )
{
    float k0 = length(p/r);
    float k1 = length(p/(r*r));
    return k0*(k0-1.0)/k1;
}

float sdCappedTorus(in vec3 p, in vec2 sc, in float ra, in float rb)
{
  p.x = abs(p.x);
  float k = (sc.y*p.x>sc.x*p.y) ? dot(p.xy,sc) : length(p.xy);
  return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb;
}

float sdRoundedCylinder( vec3 p, float ra, float rb, float h )
{
  vec2 d = vec2( length(p.xz)-2.0*ra+rb, abs(p.y) - h );
  return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rb;
}

vec3 rotate(vec3 p, float angle, vec3 axis)
{
    vec3 a = normalize(axis);
    float s = sin(angle);
    float c = cos(angle);
    float r = 1.0 - c;
    mat3 m = mat3(
        a.x * a.x * r + c,
        a.y * a.x * r + a.z * s,
        a.z * a.x * r - a.y * s,
        a.x * a.y * r - a.z * s,
        a.y * a.y * r + c,
        a.z * a.y * r + a.x * s,
        a.x * a.z * r + a.y * s,
        a.y * a.z * r - a.x * s,
        a.z * a.z * r + c
    );
    return m * p;
}

// Union, Subtraction, SmoothUnion (distance, Material) 
surface opU(surface d1, surface d2)
{
    if(d1.dist < d2.dist){
        return d1;
    } else {
    	return d2;
    }
}

float opU( float d1, float d2 ) {  return min(d1,d2); }

surface opS( surface d1, surface d2 )
{
    if(-d1.dist > d2.dist){
        d1.dist = -d1.dist;
    	return d1;
    } else {
    	return d2;
    }
}

surface opSU( surface d1, surface d2, float k ) {
    float h = clamp( 0.5 + 0.5*(d2.dist - d1.dist)/k, 0.0, 1.0 );
    float d = mix( d2.dist, d1.dist, h ) - k*h*(1.0-h);
    vec4 albedo = mix( d2.albedo, d1.albedo, h );
    return surface(d, albedo, d1.count, true);
}

float smin( float d1, float d2, float k ) {
    float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
    return mix( d2, d1, h ) - k*h*(1.0-h); 
}

//https://www.shadertoy.com/view/NdS3Dh
//SmoothSymmetricPolarMod aka smoothRot
//
//s repetitions
//m smoothness (0-1)
//c correction (0-1)
//d object displace from center
//
vec2 smoothRot(vec2 p,float s,float m,float c,float d){
  s*=0.5;
  float k=length(p);
  float x=asin(sin(atan(p.x,p.y)*s)*(1.0-m))*k;
  float ds=k*s;
  float y=mix(ds,2.0*ds-sqrt(x*x+ds*ds),c);
  return vec2(x/s,y/s-d);
}

/////////////////////////////////////////////////////////////////////////////////////////////////
// Mikka Boze Distance Function
/////////////////////////////////////////////////////////////////////////////////////////////////
float sdEar(vec3 p)
{
    p = rotate(p, RAD90+0.25, vec3(0,0,1));    
    return sdCappedTorus(p + vec3(0.05, 0.175, 0), vec2(sin(0.7),cos(0.7)), 0.03, 0.01);
}

#define EYE_SPACE 0.04

vec3 opBendXY(vec3 p, float k)
{
    float c = cos(k*p.x);
    float s = sin(k*p.x);
    mat2  m = mat2(c,-s,s,c);
    return vec3(m*p.xy,p.z);
}

vec3 opBendXZ(vec3 p, float k)
{
    float c = cos(k*p.x);
    float s = sin(k*p.x);
    mat2  m = mat2(c,-s,s,c);
    vec2 xz = m*p.xz;
    return vec3(xz.x, p.y, xz.y);
}

float sdMouse(vec3 p, float ms)
{
    vec3 q = opBendXY(p, 2.0);
    ms += 0.00001;
    return sdEllipsoid(q - vec3(0,0,0.2), vec3(0.035, 0.01 * ms,0.05 * ms));
}

float sdCheep(vec3 p)
{    
    const float x = 0.05;
    const float z = -0.175;
    const float r = 0.0045;
    const float rb1 = 100.;
    
    p = rotate(p, M_PI * -0.6 * (p.x - x), vec3(-0.2,0.8,0));
	
    float d = sdCapsule(opBendXY(p + vec3(x, -0.01, z), rb1), vec3(-0.005,0.0,0.0), vec3(0.005, 0., 0.001), r);
    float d1 = sdCapsule(opBendXY(p + vec3(x+0.01, -0.01, z), 200.0), vec3(-0.0026,0.0,0), vec3(0.0026, 0., 0), r);
    float d2 = sdCapsule(opBendXY(p + vec3(x+0.019, -0.015, z), -rb1), vec3(-0.01,0.0,-0.01), vec3(0.0045, 0., 0.0), r);
    
    return opU(opU(d, d1), d2);
}

float sdEyeBrow(vec3 p)
{
    const float x = 0.05;
    p = opBendXZ(p + vec3(0.02,0,-0.02), -6.5);
    return sdRoundBox(p + vec3(0.005, -0.14,-0.11), vec3(0.003,0.0025,0.05), 0.001);
}

surface sdBoze(vec3 p, vec3 sc, float ms)
{    
    surface result = SURF_NOHIT(1e5);
    
    float minsc = min(sc.x, min(sc.y, sc.z));
    p /= sc;
    
    // head
	  float d = sdCapsule(p, vec3(0,0.05,0), vec3(0, 0.11, 0), 0.125);
    
    float d1 = sdRoundedCylinder(p + vec3(0,0.025,0), 0.095, 0.05, 0.0);
    
    d = smin(d, d1, 0.1);
    
    vec3 mxp = vec3(-abs(p.x), p.yz);
    
    // ear
    float d2 = sdEar(mxp);
    d = opU(d, d2);

	  surface head = SURF_FACE(d);

	  // eye
    float d4 = sdCapsule(mxp, vec3(-EYE_SPACE, 0.06, 0.13), vec3(-EYE_SPACE, 0.08, 0.125), 0.0175);
    surface eye = SURF_BLACK(d4);
    
    // mouse
    float d6 = sdMouse(p, ms);
    surface mouse = SURF_MOUSE(d6);
    
    // cheep
    float d7 = sdCheep(mxp);
    surface cheep = SURF_CHEEP(d7);
    
    // eyebrows
    float d9 = sdEyeBrow(mxp);
    eye.dist = opU(eye.dist, d9);
    
    // integration
    mouse = opU(eye, mouse);
    result = opS(mouse, head);
    result = opU(cheep, result);
    
    result.dist *= minsc;
    
    return result;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// End of Mikka Boze
/////////////////////////////////////////////////////////////////////////////////////////////////

// https://iquilezles.org/articles/mandelbulb/
surface map(vec3 p)
{
	  surface result = SURF_NOHIT(1e5);
    
    float ms = sin(time) * 0.5 + 0.5;

    vec3 z = p;
	  float dz = 1.0;
    // square of the magnitude of z 
    float m2 = dot(z,z);

    vec4 orbitTrap = vec4(abs(z), m2);

    //float deg = iTime*-0.5;
    float deg=-time*0.15;
    
    float d = 1.;
    float d2 = 0.;
    float repetitions=7.0;
    float smoothness=0.1;
    float correction=0.1;
    float displace= 0.5;
    
    p = rotate(p+vec3(0,0.1,0), RAD90+time*2.1, vec3(0.5, 0, 0.5));
    
    
	  for(int i=0; i<4; i++)
    {   
        // extract sperical coordinate
        float r = length(z);
        float phi = deg*acos(z.y/r);
        float theta = deg*atan(z.x, z.z);
        
        // z = z^p+c
        z = pow(r,deg) * vec3(sin(phi)*sin(theta), cos(phi), sin(phi)*cos(theta)) + p;
        
        vec3 z2 = z;
        z2.z = abs(z2.z);
        z2.xy = smoothRot(z.xy, repetitions, smoothness, correction, displace);
        surface boze = sdBoze((z2) - vec3(0,0.1,0), vec3(3.), ms);
        result = opU(result, boze);

    }
    return result;
}

vec3 norm(in vec3 position) {
    // https://www.shadertoy.com/view/XltyRf
    vec4 n = vec4(0);
    for (int i = 0 ; i < 4 ; i++) {
        vec4 s = vec4(position, 0);
        s[i] += 0.001;
        n[i] = map(s.xyz).dist;
    }
    return normalize(n.xyz-n.w);
	
}

surface traceRay(in vec3 origin, in vec3 direction, float dist, out vec3 pos) {
    float t = 0.0;
    
    pos = origin;

    int count = 0;
    surface hit;
    float d;
    
    for (int i = 0; i < MAX_MARCH; i++) {
        hit = map(pos);
        d = hit.dist;
        
        if (d <= EPS || d >= MAX_DIST) {
        	break;
        }

        t += d;
        pos = origin + direction * t;
        count++;        
    }

    hit.dist = t;
	hit.count = count;

    pos = origin + direction * t;
        
    if(hit.isHit)
    {
        // Lighting
        vec3 normal = norm(pos);

        vec3 lightDir = normalize(vec3(cos(time), 1, sin(time)));
		    vec3 lightColor = vec3(1.5);
        
        float NoL = saturate(dot(normal, lightDir));
        
        vec3 ambientColor = vec3(0.1);
        
        hit.albedo.rgb *= NoL * lightColor +  ambientColor;
    }
    
    if(d <= EPS){
        hit.isHit = true;
        return hit;
    }else{
		
        hit.isHit = false;
        return hit;
    }
}
    
vec3 render(vec3 p, vec3 ray, vec2 uv)
{
    vec3 pos;
    surface mat = traceRay(p, ray, 0., pos);
    
    vec3 col = vec3(0,0,0);
    //vec3 sky = vec3(0.3);
    vec3 sky = sinebow(fbm(vec3(uv * 0.01, 0))*5.5+(time * M_PI2 * 0.5)*0.25)*0.5+0.25;
    
    col = mat.isHit ? mat.albedo.rgb : sky;
    
    return col;
    
}

mat3 camera(vec3 ro, vec3 ta, float cr )
{
	vec3 cw = normalize(ta - ro);
	vec3 cp = vec3(sin(cr), cos(cr),0.);
	vec3 cu = normalize( cross(cw,cp) );
	vec3 cv = normalize( cross(cu,cw) );
    return mat3( cu, cv, cw );
}

void main()
{
  vec2 r=resolution,p=(gl_FragCoord.xy*2.-r)/min(r.x,r.y);
  const float l = 3.;
  float t = time + RAD90;
    
  vec3 ro = vec3(cos(t) * l, 0.05, sin(t) * l);
  //vec3 ro = vec3(0, 0.05, 0.8);
  vec3 ta = vec3(0, -0.25, 0);
  
  mat3 c = camera(ro, ta, 0.0);
  vec3 ray = c * normalize(vec3(p, 1.5));
  vec3 col = render(ro, ray, gl_FragCoord.xy);
  
  // Output to screen
  gl_FragColor = vec4(col,1.0);
  
}