#version 450 core

uniform float now; // in milliseconds
uniform vec3 cam_pos;
uniform float time;
uniform sampler2D texObject;
uniform sampler2D texFloor;
uniform sampler2D texObject2;

out vec4 outColor;
in vec2 Texcoord;

float PI=3.14159265;

vec3 movement;

// Floor
vec2 obj_floor(in vec3 p)
{
  return vec2(p.y+20.0,0);
}

// Sphere
vec2 obj_sphere(in vec3 p)
{
  float d = length(p)-1.7;
  return vec2(d,1);
}

vec2 obj_torus(in vec3 p)
{
  vec3 o = vec3(1.0, 2.0, 0.0);
  vec2 r = vec2(2.1,0.5);
  vec2 q = vec2(length(p.xy - o.xy )-r.x,p.z - o.z);
  float d = length(q)-r.y;
  return vec2(d,1);
}

vec2 obj_union(in vec2 obj0, in vec2 obj1)
{
  if (obj0.x < obj1.x)
  	return obj0;
  else
  	return obj1;
}

vec2 obj_subtract(in vec2 obj0, in vec2 obj1)
{
  if (-obj0.x < obj1.x)
	return obj0;
  else
	return obj1;
}

vec2 obj_sphere2(in vec3 p)
{
   vec3 q = p -vec3(0,5,0);

   return obj_sphere(q);
}

vec2 sphere_torus(in vec3 p)
{
	return obj_union(obj_union(obj_sphere(p), obj_torus(p)), obj_sphere2(p));
}

vec2 obj_moving_sphere_torus(in vec3 p)
{
    vec3 q = p - movement;
    return sphere_torus(q);
}

vec2 obj_rbox(in vec3 p)
{
    vec3 q = p - vec3(0,15,-20);
    return vec2(length(max(abs(q)-vec3(40,8,0.4), vec3(0,0,0))) - 0.1, 2);
}

vec2 obj_box_plane(in vec3 p)
{
   return obj_rbox(p);
}

vec2 non_floor(in vec3 p)
{
  return obj_union(obj_moving_sphere_torus(p), obj_box_plane(p));
}
//Objects union
vec2 distance_to_obj(in vec3 p)
{
  return obj_union(obj_floor(p), non_floor(p));
}

//Floor Color (checkerboard)
vec3 floor_color(in vec3 p)
{
  if (fract(p.x*0.2)>0.2)
  {
    if (fract(p.z*0.2)>0.2)
 	return texture(texFloor, p.xy).rgb;
    else
      return vec3(0.5,0.5,0.5);
  }
  else
  {
    if (fract(p.z*.2)>.2)
      return vec3(1,1,1);
    else
      return vec3(0.3,0,0);
   }
}

vec3 prim_c(in vec3 p)
{
  return texture(texObject, p.xy).xyz;
}

vec3 second_c(in vec3 p)
{
  return textureLod(texObject2, vec2(1-p.x, 1-p.y) / vec2(5,5), 1).xyz;
}

vec3 gamma_correct(in vec3 c)
{
  return pow(c, vec3(1/2.2));
}

void main(void)
{

  float ts = now / 1000.0;


  vec2 vPos = -1.0 + 2.0 * Texcoord;

  // Camera up vector.
  vec3 vuv=vec3(0,1,0); 
  
  // Camera lookat.
  vec3 vrp=vec3(0,3,0);

  float beats = ts * 60 / 147;
  movement = vec3(sin(beats) *5,0,cos(ts) * 8);
  vec3 prp = vec3(6.0, 4, 8.0);
  // Camera setup.
  vec3 vpn=normalize(vrp-prp);
  vec3 u=normalize(cross(vuv,vpn));
  vec3 v=cross(vpn,u);
  vec3 vcv=(prp+vpn);
  vec3 scrCoord=vcv+vPos.x*u+vPos.y*v;
  vec3 scp=normalize(scrCoord-prp);

  // Raymarching.
  const vec3 e=vec3(0.02,0,0);
  const float maxd=100.0; //Max depth
  vec2 d=vec2(0.02,0.0);
  vec3 c,p,N;

  float f=1.0;
  for(int i=0;i<256;i++)
  {
    if ((abs(d.x) < .001) || (f > maxd)) 
      break;
    
    f+=d.x;
    p=prp+scp*f;
    d = distance_to_obj(p);
  }
  
  if (f < maxd)
  {
    // y is used to manage materials.
    if (d.y==0) 
      c=floor_color(p);
    else if (d.y == 1)
  	    c=prim_c(p);
    else if (d.y == 2)
    		c=second_c(p);
    
    vec3 n = vec3(d.x-distance_to_obj(p-e.xyy).x,
                  d.x-distance_to_obj(p-e.yxy).x,
                  d.x-distance_to_obj(p-e.yyx).x);
    N = normalize(n);
    float b=dot(N,normalize(prp-p));
    //simple phong lighting, LightPosition = CameraPosition
    outColor=vec4(gamma_correct((b*c+pow(b,16.0))*(1.0-f*.01)),1.0);
  }
  else 
   outColor=vec4(0,0,0,1); //background color
}
