uniform sampler2D iChannel0;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform vec2 iResolution;
uniform float iGlobalTime;
uniform float cameraRotation_x;
uniform float cameraRotation_y;
uniform float cameraRotation_z;
uniform float cameraPosition_x;
uniform float cameraPosition_y;
uniform float cameraPosition_z;

#define E 1e-3
#define FAR_CLIP 8
#define AO 6
#define STEPS 60
float time=iGlobalTime*0.8;

// various helper functions
float imp(float k,float x){return k*x*exp(1.0-k*x);}
// rotation matrixes
mat3 rx(float a){return mat3(1.0,0.0,0.0,0.0,cos(a),-sin(a),0.0,sin(a),cos(a));}
mat3 ry(float a){return mat3(cos(a),0,sin(a),0.0,1.0,0.0,-sin(a),0.0,cos(a));}
mat3 rz(float a){return mat3(cos(a),-sin(a),0.0,sin(a),cos(a),0.0,0.0,0.0,1.0);}
// white noise
float hash(float c){return fract(sin(dot(c,12.9898))*43758.5453);}

// cheap but sufficient fake perlin noise
float no(vec3 x){
	vec3 p=floor(x);
	vec3 f=fract(x);

	f=f*f*(3.0-2.0*f);
	float n=p.x+p.y*57.0+113.0*p.z;
	return mix(mix(mix(hash(n),hash(n+1.0),f.x),
				mix(hash(n+57.0),hash(n+58.0),f.x),f.y),
			mix(mix(hash(n+113.0),hash(n+114.0),f.x),
				mix(hash(n+170.0),hash(n+171.0),f.x),f.y),f.z);
}

#define NUMI 11
#define NUMF 11.0
vec3 fishPos = vec3(0);
float fishTime = iGlobalTime*0.6;
float isJump;
float isJump2;
vec3 ccd, ccp;

vec2 sd2Segment( vec3 a, vec3 b, vec3 p )
{
	vec3  pa = p - a;
	vec3  ba = b - a;
	float t = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
	vec3  v = pa - ba*t;
	return vec2( dot(v,v), t );
}

float udBox(vec3 p, vec3 b)
{
    return length(max(abs(p)-b, 0.0));
}

float udRoundBox( vec3 p, vec3 b, float r )
{
    return length(max(abs(p)-b,0.0))-r;
}

float smin( float a, float b, float k )
{
	float h = clamp( 0.5 + 0.5*(b-a)/k, 0.0, 1.0 );
	return mix( b, a, h ) - k*h*(1.0-h);
}

vec3 anima2( void )
{
    vec3 a1 = vec3(0.0,        sin(3.0*fishTime+6.2831/4.0),0.0);
    vec3 a2 = vec3(0.0,1.5+2.5*cos(1.0*fishTime),0.0);
	vec3 a = mix( a1, a2, isJump );
	a.y *= 0.5;
	a.x += 0.1*sin(0.1 - 1.0*fishTime)*(1.0-isJump);
    return a;
}

vec2 anima( float ih, float t )
{
    float an1 = 0.9*(0.5+0.2*ih)*cos(5.0*ih - 3.0*t + 6.2831/4.0);
    float an2 = 1.0*cos(3.5*ih - 1.0*t + 6.2831/4.0);
    float an = mix( an1, an2, isJump );
    float ro = 0.4*cos(4.0*ih - 1.0*t)*(1.0-0.5*isJump);
	return vec2( an, ro );
}

vec2 dolphin( vec3 p )
{
    vec2 res = vec2( 1000.0, 0.0 );

	vec3 a = vec3(0);
	
	float or = 0.0;
	float th = 0.0;
	float hm = 0.0;

	vec3 p1 = a; vec3 d1=vec3(0.0);
	vec3 p2 = a; vec3 d2=vec3(0.0);
	vec3 p3 = a; vec3 d3=vec3(0.0);
	vec3 mp = a;
	for( int i=0; i<NUMI; i++ )
	{	
		float ih = float(i)/NUMF;
		vec2 anim = anima( ih, fishTime );
		float ll = 0.48; if( i==0 ) ll=0.655;
		vec3 b = a + ll*normalize(vec3(sin(anim.y), sin(anim.x), cos(anim.x)));
		
		vec2 dis = sd2Segment( a, b, p );

		if( dis.x<res.x ) {res=vec2(dis.x,ih+dis.y/NUMF); mp=a+(b-a)*dis.y; ccd = b-a;}
		
		if( i==3 ) { p1=a; d1 = b-a; }
		if( i==4 ) { p3=a; d3 = b-a; }
		if( i==(NUMI-1) ) { p2=b; d2 = b-a; }

		a = b;
	}
	ccp = mp;
	
	float h = res.y;
	float ra = 0.05 + h*(1.0-h)*(1.0-h)*2.7;
	ra += 7.0*max(0.0,h-0.04)*exp(-30.0*max(0.0,h-0.04)) * smoothstep(-0.1, 0.1, p.y-mp.y);
	ra -= 0.03*(smoothstep(0.0, 0.1, abs(p.y-mp.y)))*(1.0-smoothstep(0.0,0.1,h));
	ra += 0.05*clamp(1.0-3.0*h,0.0,1.0);
    ra += 0.035*(1.0-smoothstep( 0.0, 0.025, abs(h-0.1) ))* (1.0-smoothstep(0.0, 0.1, abs(p.y-mp.y)));
	
	// body
	res.x = 0.75 * (distance(p,mp) - ra);
	//res.x = 0.75 * (res.x*3.0 - ra);

    // fin	
	d3 = normalize(d3);
	float k = sqrt(1.0 - d3.y*d3.y);
	mat3 ms = mat3(  d3.z/k, -d3.x*d3.y/k, d3.x,
				        0.0,            k, d3.y,
				    -d3.x/k, -d3.y*d3.z/k, d3.z );
	vec3 ps = p - p3;
	ps = ms*ps;
	ps.z -= 0.1;
    float d5 = length(ps.yz) - 0.9;
	d5 = max( d5, -(length(ps.yz-vec2(0.6,0.0)) - 0.35) );
	d5 = max( d5, udRoundBox( ps+vec3(0.0,-0.5,0.5), vec3(0.0,0.5,0.5), 0.02 ) );
	res.x = smin( res.x, d5, 0.1 );

	
    // fin	
	d1 = normalize(d1);
	k = sqrt(1.0 - d1.y*d1.y);
	ms = mat3(  d1.z/k, -d1.x*d1.y/k, d1.x,
				   0.0,            k, d1.y,
               -d1.x/k, -d1.y*d1.z/k, d1.z );
	ps = p - p1;
	ps = ms*ps;
	ps.x = abs(ps.x);
	float l = ps.x;
	l=clamp( (l-0.4)/0.5, 0.0, 1.0 );
	l=4.0*l*(1.0-l);
	l *= 1.0-clamp(5.0*abs(ps.z+0.2),0.0,1.0);
	ps.xyz += vec3(-0.2,0.36,-0.2);
    d5 = length(ps.xz) - 0.8;
	d5 = max( d5, -(length(ps.xz-vec2(0.2,0.4)) - 0.8) );
	d5 = max( d5, udRoundBox( ps+vec3(0.0,0.0,0.0), vec3(1.0,0.0,1.0), 0.015+0.05*l ) );
	res.x = smin( res.x, d5, 0.12 );
	
    // tail	
	d2 = normalize(d2);
	mat2 mf = mat2( d2.z, d2.y, -d2.y, d2.z );
	vec3 pf = p - p2 - d2*0.25;
	pf.yz = mf*pf.yz;
    float d4 = length(pf.xz) - 0.6;
	d4 = max( d4, -(length(pf.xz-vec2(0.0,0.8)) - 0.9) );
	d4 = max( d4, udRoundBox( pf, vec3(1.0,0.005,1.0), 0.005 ) );
	res.x = smin( res.x, d4, 0.1 );
	
    res.x -= 0.15*tan(res.x);
    
	return res;
}

float geometry(vec3 r){
    return dolphin(r).x;
}

vec3 normal(vec3 p){
	float e=1e-3;
	return normalize(vec3(geometry(p+vec3(e,0,0))-geometry(p-vec3(e,0,0)),
	                      geometry(p+vec3(0,e,0))-geometry(p-vec3(0,e,0)),
	                      geometry(p+vec3(0,0,e))-geometry(p-vec3(0,0,e))));
}

vec3 march(vec3 origin, vec3 direction){
	vec3 position = vec3(0.0);
	float dist = 0.0;
	float length = 0.0;
	float glow = 0.0;
	for(int i=0; i < STEPS; ++i){
		position = origin + direction * length;
		dist = geometry(position);
		length  += dist;
		if(float(FAR_CLIP) < length || dist < E) break;
	}
    
    if(float(FAR_CLIP) < length) return vec3(1);
    return vec3(distance(origin, position)/float(FAR_CLIP));
}

void mainImage(out vec4 fragColor, in vec2 fragCoord){
    vec2 r = iResolution.xy;
	vec2 p = 1.-2.*fragCoord.xy/r.xy;
    p.y *= r.y/r.x;

	// camera setup
	mat3 angle     = rx(-.25)*ry(2.0)*rz(.06);

    mat3 rotation  = rz(cameraRotation_x)*ry( cameraRotation_y)*rz(cameraRotation_z);
	vec3 camera    = vec3(-4.0+cameraPosition_x, -1.9+cameraPosition_y, 1.0+cameraPosition_z);
	vec3 direction = normalize(vec3(p, 1.5)*angle*rotation);
	
	// render visuals
	vec3 color = march(camera, direction);
	fragColor = vec4(color, 1.0);
}

void main() {
	vec4 fragColor = vec4( 0.);
	vec2 fragCoord = gl_FragCoord.xy;
    mainImage( fragColor, fragCoord);
	gl_FragColor = fragColor;
}