#version 450

layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 out_color;
layout(set = 2, binding = 0) uniform sampler2D screen;
layout(set = 2, binding = 1) uniform sampler2D depth_tex;

layout(std140, set = 3, binding = 0) uniform UniformBlock {
  mat4  model;
  mat4  view;
  mat4  projection;
  mat4  inv_view;
  mat4  inv_projection;
  vec4 time_padded;
  vec4  cam_pos;
};

const int MAX_MARCH_STEPS = 64;
const float EPS_DIST = 1e-6;


float sdf(vec3 point){
  vec3 p = point - cam_pos.xyz;
  const float speed = 1.0 + exp(sin(time_padded.x));
  float t = time_padded.x * speed;
  const int N = 5;
  float d = 1.0/EPS_DIST;
  float orbit_radius = 0.4;
  float radius = 0.03;
  for(int i = 0; i < N; i++) {
    float phase = ((3.14159 * 2.0) / float(N)) * float(i);
    d = min(d, length(p - orbit_radius*vec3(sin(t + phase), 0.0, cos(t + phase))) - radius);
  }

  return d;
}

vec3 getnormal(vec3 p) {
  float e = EPS_DIST;
  return normalize(vec3(
    sdf(p + vec3(e, 0.0, 0.0)) - sdf(p - vec3(e, 0.0, 0.0)),
    sdf(p + vec3(0.0, e, 0.0)) - sdf(p - vec3(0.0, e, 0.0)),
    sdf(p + vec3(0.0, 0.0, e)) - sdf(p - vec3(0.0, 0.0, e))
  ));
}

vec4 march(vec3 point, vec3 ray, float max_depth) {
  float totaldist = 0.0;

  int i;
  for (i = 0; i < MAX_MARCH_STEPS && totaldist < max_depth; i += 1) {
    float h = sdf(point);
    
    if (h < EPS_DIST) {
      totaldist += h;
      return vec4(point, totaldist);
    }
    
    totaldist += h;
    if (totaldist > max_depth)
      break;
      
    point += h * ray;
  }

  return vec4(-1.0);
}

vec3 camray() {
  vec2 ndc = uv * 2.0 - 1.0;
  ndc.y = -ndc.y;
  vec4 view_pos = inv_projection * vec4(ndc, 1.0, 1.0);
  view_pos /= view_pos.w;
  return normalize((inv_view * vec4(normalize(view_pos.xyz), 0.0)).xyz);
}

vec4 kernel(float max_depth) {
  max_depth = (max_depth > 0.0)? max_depth : 1000.0;
  vec3 cam_ray = camray();

  return march(cam_pos.xyz, cam_ray, max_depth);
}

float linearize_depth(vec2 uv) {
  float d = texture(depth_tex, uv).r;
  if (d >= 1.0) return 1000.0;
  vec4 ndc = vec4(uv * 2.0 - 1.0, d * 2.0 - 1.0, 1.0);
  vec4 view_pos = inv_projection * ndc;
  view_pos /= view_pos.w;
  return -view_pos.z; // view space, positive distance
}


void main() {
  float max_depth = linearize_depth(uv);
  vec4 hit = kernel(max_depth);

  if (hit.w > 0.0) {
   out_color = vec4(1.0);//out_color = vec4(getnormal(hit.xyz), 1.0);
  } else {
    out_color = texture(screen, uv);
  }
}

