#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;

const float MATH_PI = float( 3.14159265359 );
const vec3 SKY_COLOR = vec3(0.05, 0.2, 0.5);
const vec3 LIGHT = normalize( vec3(  0.1, 0.8,  0.9 ) );
const vec3 LIGHT_BACK = LIGHT * - 1.0;
const float AIRPLANE_SIZE = 20.0; // AIRPLANE描画範囲直径
const float AIRPLANE_SCALE = 1.0; // AIRPLANEスケール
const float EP = 0.0001;
const int AIRPLANE_COUNT = 5;
const float PI = 3.141592;
const float PI2 = PI * 2.0;


const vec2 cloudrange = vec2(0.0, 10000.0);
const vec3 cloudmove = vec3(0.0 , 0.0, 600.0);

mat3 airplanes[AIRPLANE_COUNT];

float dot2(in vec2 v ) { return dot(v,v); }
float dot2(in vec3 v ) { return dot(v,v); }

// random/hash function              
float hash( float n )
{
  return fract(cos(n)*41415.92653);
}

// 3d noise function
float noise( in vec3 x )
{
  vec3 p  = floor(x);
  vec3 f  = smoothstep(0.0, 1.0, fract(x));
  float n = p.x + p.y*57.0 + 113.0*p.z;

  return mix(mix(mix( hash(n+  0.0), 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);
}

mat3 m = mat3( 0.00,  1.60,  1.20, -1.60,  0.72, -0.96, -1.20, -0.96,  1.28 );

// Fractional Brownian motion
float fbm( vec3 p )
{
  float f = 0.5000*noise( p ); p = m*p*1.1;
  f += 0.2500*noise( p ); p = m*p*1.2;
  f += 0.1666*noise( p ); p = m*p;
  f += 0.0834*noise( p );
  return f;
}

vec3 foldXZ(vec3 p)
{
    p.x = abs(p.x);
    p.z = abs(p.z);
    return p;
}

mat2 rotate(float a)
{
    float s = sin(a),c = cos(a);
    return mat2(c, s, -s, c);
}

vec4 dElongate( in vec3 p, in vec3 h )
{
    vec3 q = abs(p)-h;
    return vec4( max(q,0.0), min(max(q.x,max(q.y,q.z)),0.0) );
}

float dSphere(vec3 pos, float size)
{
    return length(pos) - size;
}

float dEllipsoid(vec3 pos, vec3 size)
{
    return dSphere(pos / size, 1.0);
}

float dRoundCone( in vec3 p, in float r1, float r2, float h )
{
    vec2 q = vec2( length(p.xz), p.y );
    
    float b = (r1-r2)/h;
    float a = sqrt(1.0-b*b);
    float k = dot(q,vec2(-b,a));
    
    if( k < 0.0 ) return length(q) - r1;
    if( k > a*h ) return length(q-vec2(0.0,h)) - r2;
        
    return dot(q, vec2(a,b) ) - r1;
}

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

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

float dTriangle( vec3 p, vec3 a, vec3 b, vec3 c )
{
    vec3 ba = b - a; vec3 pa = p - a;
    vec3 cb = c - b; vec3 pb = p - b;
    vec3 ac = a - c; vec3 pc = p - c;
    vec3 nor = cross( ba, ac );

    return sqrt(
    (sign(dot(cross(ba,nor),pa)) +
     sign(dot(cross(cb,nor),pb)) +
     sign(dot(cross(ac,nor),pc))<2.0)
     ?
     min( min(
     dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa),
     dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ),
     dot2(ac*clamp(dot(ac,pc)/dot2(ac),0.0,1.0)-pc) )
     :
     dot(nor,pa)*dot(nor,pa)/dot2(nor) );
}

float dTree(vec3 po, inout vec4 hitColor)
{
    vec3 p = po;
    // p = vec3(floor(p.x),floor(p.y),floor(p.z));
    p.x = mod(p.x, 20.) - 10.0;
    p.z = mod(p.z, 20.) - 10.0;

    vec3 size = vec3(0.4, 4.0, 0.4);
    float branch = dBox(p, size);
    for (int i = 0; i < 2; i++)
    {
        vec3 q = foldXZ(p);
        q.y -= size.y;
        q.xy *= rotate(-0.4);
        q.zy *= rotate(-0.4);
        branch = min(branch, dBox(q, size));
        p = q;
    }

    p.y -= size.y;
    float leaf = dEllipsoid(p, vec3(3,2,3));

    if (branch < leaf)
    {
        hitColor = vec4(0.44, 0.29, 0.22, 1.0);
    }
    else
    {
        float hoge = step(fract(po.x) + fract(po.y) + fract(po.z), 0.9) * 0.5;
        hitColor = vec4(0.44 - hoge, 0.99 - hoge, 0.22 - hoge, 1.0);
    }

    return min(branch, leaf);
}

vec3 rotateX(vec3 position, float rotate)
{
    return vec3(position.x, position.z * -sin(rotate) + position.y * cos(rotate), position.z * cos(rotate) + position.y * sin(rotate));
}

float dAircraft(vec3 ray, inout vec4 hitColor)
{
    vec3 body1 = vec3(0.5, 0.6, 2.0) * AIRPLANE_SCALE;
    vec3 body1Pos = vec3(0, 0.2, -1.) * AIRPLANE_SCALE;
    float body1Rot = 3.14 * -0.03;
    vec3 body2 = vec3(1, 1, 5) * AIRPLANE_SCALE;
    vec3 body2Pos = vec3(0, 1, 0) * AIRPLANE_SCALE;
    float body2Rot = 3.14 * 0.0;
    vec3 body3 = vec3(0.5, 0.9, 7) * AIRPLANE_SCALE;
    vec3 body3Pos = vec3(1, -1, 1.1) * AIRPLANE_SCALE;
    float body3Rot = 3.14 * 0.5;
    vec2 body4 = vec2(0.3, 0.1) * AIRPLANE_SCALE;
    vec3 body4Pos = vec3(-1.5, 1.5, 1) * AIRPLANE_SCALE;
    float body4Distortion = 3.14 * 0.0;
    vec2 body5 = vec2(8.0, -6.0) * AIRPLANE_SCALE;
    vec3 body5Pos = vec3(0, -1, 0) * AIRPLANE_SCALE;
    vec2 body6 = vec2(4.0, -3.0) * AIRPLANE_SCALE;
    vec3 body6Pos = vec3(0, -1.1, -5) * AIRPLANE_SCALE;
    vec3 body7 = vec3(2.4, 0.5, 2.0) * AIRPLANE_SCALE;
    vec3 body7Pos = vec3(0, 1.5, 5) * AIRPLANE_SCALE;
    vec3 body8_1 = vec3(1.2, 0, -4.0) * AIRPLANE_SCALE;
    vec3 body8_2 = vec3(2.4, 2.5, -7.5) * AIRPLANE_SCALE;
    vec3 body8_3 = vec3(1.4, -1., -7.5) * AIRPLANE_SCALE;
    vec3 body8Pos = vec3(0, -1, 0) * AIRPLANE_SCALE;

    float body1Depth = dEllipsoid(rotateX(ray, body1Rot) + body1Pos, body1);
    float body2Depth = dEllipsoid(rotateX(ray, body2Rot) + body2Pos, body2);
    float body3Depth = dRoundCone(rotateX(vec3(ray.x, ray.y, ray.z), body3Rot) + body3Pos, body3.x, body3.y, body3.z);
    // absしたい
    float body3_1Depth = dRoundCone(rotateX(vec3(-ray.x, ray.y, ray.z), body3Rot) + body3Pos, body3.x, body3.y, body3.z);
    vec4 body4_ = dElongate(vec3(abs(ray.x), ray.y, ray.z) + body4Pos + vec3(0, 0, ray.y * -0.5), vec3(0.6, 0.1, 2.0));
    float body4Depth = body4_.w + dTorus(rotateX(body4_.xzy + vec3(0, 0, 0), body4Distortion), body4);
    float body5Depth = dTriangle(ray, vec3(body5.x, 0.0, body5.y) + body5Pos, vec3(-body5.x, 0.0, body5.y) + body5Pos, body5Pos) - 0.3;
    float body6Depth = dTriangle(ray, vec3(body6.x, 0.0, body6.y) + body6Pos, vec3(-body6.x, 0.0, body6.y) + body6Pos, body6Pos) - 0.2;
    float body7Depth = dBox(ray + body7Pos + vec3(0, 0, ray.y * -0.5), body7);
    float body8Depth = dTriangle(vec3(abs(ray.x), ray.y, ray.z), body8_1 + body8Pos, body8_2 + body8Pos, body8_3 + body8Pos) - 0.1;

    hitColor = vec4(1.0, 1.0, 1.0, 1.0);
    float depth = min(min(min(min(min(min(min(min(body1Depth, body2Depth), body3Depth), body3_1Depth), body4Depth), body5Depth), body6Depth), body7Depth), body8Depth);

    if (EP > abs(depth - body1Depth))
    {
        hitColor = vec4(0.2, 0.2, 0.2, 1.0);
    }

    return depth;
}

vec3 dAircraftNormal(vec3 pos)
{
    vec4 _tmp;
    return normalize(vec3(
            dAircraft(pos, _tmp) - dAircraft(vec3(pos.x - EP, pos.y, pos.z), _tmp),
            dAircraft(pos, _tmp) - dAircraft(vec3(pos.x, pos.y - EP, pos.z), _tmp),
            dAircraft(pos, _tmp) - dAircraft(vec3(pos.x, pos.y, pos.z - EP), _tmp)
        ));
}

mat4 getWorldMat(mat3 transform)
{
    // trans world
    vec3 axisX = cross(transform[1], transform[2]) * -1.0;

    return mat4(
        axisX.x, axisX.y, axisX.z, 0,
        transform[1].x, transform[1].y, transform[1].z, 0,
        transform[2].x, transform[2].y, transform[2].z, 0,
        0, 0, 0, 1 
        );
}

void airplaneRender(inout vec4 color, inout float depth, float animTime, vec3 cameraPos, vec3 rayDirection)
{
    for (int i = 0; i < AIRPLANE_COUNT; i++)
    {
        mat4 worldMat = getWorldMat(airplanes[i]);

        vec3 localCameraPosition = (worldMat * vec4(cameraPos - airplanes[i][0], 1.0)).xyz;
        vec3 localRayDirection = (worldMat * vec4(rayDirection, 1.0)).xyz;

        float airplaneDepth = length(localCameraPosition) - AIRPLANE_SIZE * 0.5;
        vec4 hitColor = color;

        float marchDepth = 0.0;
        for (int i = 0; i < 1000; i++)
        {
            if (marchDepth > airplaneDepth + AIRPLANE_SIZE || depth < marchDepth)
            {
                break;
            }

            vec3 ray = localCameraPosition + localRayDirection * marchDepth;
            float diff = dAircraft(ray, hitColor);

            if (diff < EP)
            {
	    	depth = marchDepth + diff;
	   	vec3 normal = dAircraftNormal(ray);
	    	color = vec4(vec3(clamp(dot(normal, LIGHT) * hitColor, 0.0, 1.0)) + 0.2, 1.0);
            }
            else
            {
                // 細かい所に精度をよせる
                marchDepth += diff;
            }

            marchDepth += AIRPLANE_SIZE * 0.001;
        }
    }
}

void skyRender(inout vec4 color, inout float depth, float animTime, vec3 cameraPos, vec3 rayDirection)
{
    float sundot = clamp(dot(rayDirection, LIGHT),0.0,1.0);
        
    // render sky
    float t = pow(1.0-0.7*rayDirection.y, 15.0);
    vec3 col = 0.8 * SKY_COLOR;
    // sun
    col += 0.47*vec3(1.6,1.4,1.0)*pow( sundot, 350.0 );
    // sun haze
    col += 0.4*vec3(0.8,0.9,1.0)*pow( sundot, 2.0 );
        
    // CLOUDS
    vec4 sum = vec4(0,0,0,0);
    float c = 0.;
    for(int i=0;i<1000;i++) // or while
    { 
        c +=100.; // cloud depth
        vec3 cpos = cameraPos + c*rayDirection + cloudmove * time; // cloud position
        if(cloudrange.x < cpos.y && cpos.y < cloudrange.y){
            float alpha = smoothstep(0.5, 1.0, fbm( cpos*0.00025 ))*.9; // fractal cloud density
            vec3 localcolor = mix(vec3( 1.1, 1.05, 1.0 ), 0.7*vec3( 0.4,0.4,0.3 ), alpha); // density color white->gray
            alpha = (1.0-sum.w)*alpha; // alpha/density saturation (the more a cloud layer's density, the more the higher layers will be hidden)
            sum += vec4(localcolor*alpha, alpha); // sum up weightened color
        }
        
        if (0. < rayDirection.y && cloudrange.y < cpos.y)
        break;
        if (rayDirection.y < 0. && cpos.y < cloudrange.x)
        break;
        if (0.98 < sum.w)
        break;
        if (depth < c)
        break;
    }
        


    float alpha = smoothstep(0.7, 1.0, sum.w);
    sum.rgb /= sum.w+0.0001;

    sum.rgb -= 0.6*vec3(0.8, 0.75, 0.7)*pow(sundot,13.0)*alpha;
    sum.rgb += 0.2*vec3(1.3, 1.2, 1.0)* pow(sundot,5.0)*(1.0-alpha);

    col = mix( col, sum.rgb , sum.w );

    color = color + vec4(col, 1.0);
}

void objectRender(inout vec4 color, inout float depth, float animTime, vec3 cameraPos, vec3 rayDirection)
{
    airplaneRender(color, depth, animTime, cameraPos, rayDirection);
}

vec3 getAirplanePosition(float animTime, int index)
{
    vec3 line = vec3(0, 5000., 500);
    vec3 line2 = vec3(cos(animTime * 0.2) * 1000.0 - 250., 20, sin(animTime * 0.2 - 10.0) * 1000.0);
    vec3 member = vec3(float(index) * 20., 10.0, float(index) * 100.);
    return line + line2 + member;
}

void objectUpdate(float animTime)
{
    for(int index = 0; index < AIRPLANE_COUNT; index++)
    {
        vec3 position = getAirplanePosition(animTime, index);
        vec3 prevPosition = getAirplanePosition(animTime - EP, index);
        vec3 nextPosition = getAirplanePosition(animTime + EP, index);
        vec3 dir = normalize(nextPosition - position);
        vec3 top = vec3(0, 1, 0);
        airplanes[index] = mat3(position, top, dir);
    }
}

void cameraUpdate(vec2 fragCoord, float animTime, out vec3 cameraPosition, out vec3 rayDirection)
{
    vec2 uv = 2. * fragCoord.xy / resolution.xy - 1.0;
    uv.x *= resolution.x / resolution.y;
    
    cameraPosition = vec3(sin(1.0*animTime -10.0) * 100., 5000., 0.0);

    vec3 cameraDirection = normalize(airplanes[0][0] - cameraPosition);
    vec3 cameraTop = normalize(cameraDirection + vec3(0, 1, 0));
    vec3 right = normalize(cross(cameraDirection, cameraTop));
    cameraTop = cross(cameraDirection, right) * -1.0;

    rayDirection = normalize(uv.x * right + uv.y * cameraTop + cameraDirection);
}

void main( void )
{    
    float animTime = time;
    vec3 cameraPos;
    vec3 rayDirection;
    vec4 color;
    float depth = 100000.0;

    objectUpdate(animTime);
    cameraUpdate(gl_FragCoord.xy, animTime, cameraPos, rayDirection);

    objectRender(color, depth, animTime, cameraPos, rayDirection);
    skyRender(color, depth, animTime, cameraPos, rayDirection);

    gl_FragColor = color;
}