
#extension GL_EXT_frag_depth : enable

precision mediump float;

varying vec3 v_texCoord;
//varying vec2 v_uv;

uniform vec3 u_pos;
uniform mat3 u_inverse_view;
uniform samplerCube u_sky;


//@{vertex

attribute vec2 a_position;

void main()
{
  v_texCoord = u_inverse_view * vec3(a_position, 1.0);

  //v_uv = a_position;
  gl_Position = vec4(a_position, 0.0, 1.0);
}
//@}

//@{fragment


#define PI 3.14159265
#define TAU (2.0*PI)
#define PHI (1.618033988749895)
// PHI (sqrt(5)*0.5 + 0.5)


float vmax(vec3 v) {
  return max(max(v.x, v.y), v.z);
}



// Box: correct distance to corners
float fBox(vec3 p, vec3 b) {
  vec3 d = abs(p) - b;
  return length(max(d, 0.)) + vmax(min(d, 0.));
}

// Repeat in two dimensions
vec2 pMod2(inout vec2 p, vec2 size) {
  vec2 c = floor((p + size*0.5)/size);
  p = mod(p + size*0.5,size) - size*0.5;
  return c;
}


const int iterations = 160;
const float dist_eps = .001;
const float ray_max = 2000.0;
const float fog_density = .001;


float fField(vec3 p);

vec3 dNormal(vec3 p)
{
  const vec2 e = vec2(.005,0);
  return normalize(vec3(
                     fField(p + e.xyy) - fField(p - e.xyy),
                     fField(p + e.yxy) - fField(p - e.yxy),
                     fField(p + e.yyx) - fField(p - e.yyx) ));
}

vec4 trace(vec3 ray_start, vec3 ray_dir, out float ray_len)
{
  ray_len = 0.0;
  vec3 p = ray_start;
  for(int i=0; i<iterations; ++i) {
    float dist = fField(p);
    if (dist < dist_eps)    return vec4(p, 1.0);
    if (ray_len > ray_max) return vec4(0.0);
    p += dist*ray_dir;
    ray_len += dist;
  }
  return vec4(p, 0.0);
}


vec3 sky_color(vec3 ray_dir)
{
  return textureCube(u_sky, ray_dir).xyz;
}

vec3 shade(vec3 ray_start, vec3 ray_dir, vec3 light_dir, vec4 hit, float ray_len)
{
  vec3 fog_color = sky_color(ray_dir);
   
  vec3 color;
  if (hit.w == 0.0) {
    ray_len = 1e16;
    color = fog_color;
  } else {
    vec3 dir = hit.xyz - ray_start;
    vec3 norm = dNormal(hit.xyz);
    float diffuse = max(0.0, dot(norm, light_dir));
    float spec = max(0.0,dot(reflect(light_dir,norm),normalize(dir)));
    spec = pow(spec, 6.0)*.5;
       

    vec3 base_color = vec3(0.,0.04,0.2);

    color = mix(vec3(0.,.1,.3),vec3(1.,1.,.9),diffuse)*base_color +
      spec*vec3(1.,1.,.9);


    float fog_dist = ray_len;
    float fog = 1.0 - 1.0/exp(fog_dist*fog_density);
    color = mix(color, fog_color, fog);

    vec3 refdir = reflect(dir, norm);
    float dist;
//vec4 refl = trace(hit.xyz+refdir, refdir, dist);
//if(refl.w ==0.) 
    {
      color += sky_color(refdir) * 0.3;
    }

  }
   
   
 
  return color;
}


void main()
{
    
  const vec3 light_dir = normalize(vec3(.5, 1.0, -.25));
   
  vec3 pos = u_pos;
   
  vec3 dir = normalize(v_texCoord);

  float z;
  vec4 hit = trace(pos, dir,z);
  vec3 color = shade(pos, dir, light_dir, hit, z);
  color = pow(color,vec3(.44));
  gl_FragColor = vec4(color, 1.);
  gl_FragDepthEXT = clamp((1./z - 1.)/(1./2000. - 1.), -1., 0.9999) / 2. + .5;

}


float fField(vec3 p)
{
  vec2 q = pMod2(p.xz,vec2(11.5));

  float h = 20.  *abs( sin(q.x/TAU) * sin(q.y/TAU) + sin(q.x/TAU * 3.) * sin(q.y/TAU * 2.));
  float d = p.y - h;
    

  if(d > 4.5) return d;
    

  float box =  fBox(p,vec3(2.0,h,2.));
  if(p.y < 20.) box = min(box, 5.);
  return min(box -  sin(p.x)*sin(p.z)*0.2, p.y);

}


//@} 
