#version 330 core

#define FOV 8

#define EPSILON 0.01
#define MAX_STEPS 64
#define NEAR_D 0.
#define FAR_D 40.

#define GRAD_EPSILON 0.0001

#define saturate(x) clamp(x, 0, 1)

uniform vec2 u_Resolution;
uniform vec4 u_Mouse;
uniform float u_Time;

out vec4 color;

#include "hg_sdf.glsl"
#include "utils.glsl"
#include "march_prolog.glsl"
#include "colormap_cool.glsl"

vec3 ray_dir(float fov, vec2 uv) {
	float z = 1./tan(radians(fov)/2.);
	return normalize(vec3(uv, z));
}

SceneResult scene_f(vec3 p) {
	SceneResult box = SceneResult(fSphere(p, 2), 0.);

	return box;
}

vec3 estimate_scene_normal(vec3 p) {
	vec3 dx = vec3(GRAD_EPSILON, 0, 0);
	vec3 dy = vec3(0, GRAD_EPSILON, 0);
	vec3 dz = vec3(0, 0, GRAD_EPSILON);

	return normalize(vec3(
		scene_f(p + dx).d - scene_f(p - dx).d,
		scene_f(p + dy).d - scene_f(p - dy).d,
		scene_f(p + dz).d - scene_f(p - dz).d
	));
}

vec3 raymarch(vec3 o, vec3 d, float start, float end) {
	float t = start;
	for (int i = 0; i < MAX_STEPS; i++) {
		SceneResult sr = scene_f(o + d*t);
		if (sr.d < EPSILON || t > end)
			return vec3(t, sr.mat_idx, i);

		t += sr.d;
	}
	return vec3(end, N_HIT_FAR_PLANE, MAX_STEPS);
}

vec4 flag(float p) {
	p = abs(p);
	if (p < 0.167) {
		return vec4(1);
	} else if (p < 0.5) {
		return vec4(0.960784, 0.662745, 0.721569, 1);
	} else {
		return vec4(0.356863, 0.807843, 0.980392, 1);
	}
}

vec4 shade_material(vec3 p, vec3 norm, float mat_idx) {
	if (mat_idx == 0.) {
		return flag(p.y/(sin(p.z)+1));
	}
}

vec4 shade(vec3 p, float mat_idx) {
	vec3 norm = estimate_scene_normal(p);
	vec4 color_mat = shade_material(p, norm, mat_idx);

	return color_mat;
}

void main() {
	vec2 mouse_uv = u_Mouse.xy * 2.0 / u_Resolution.xy - 1.0;

	vec2 uv = gl_FragCoord.xy * 2.0 / u_Resolution.xy - 1.0;
	uv.x *= u_Resolution.x/u_Resolution.y;

    float an = 0.3 * u_Time;
    float d = 6.;
	vec3 eye = vec3(3. * sin(an), 2., 3. * cos(an)) * d;
	vec3 target = vec3(0., 0., 0.);
	mat3 lookAt = look_mat(eye, target, 0);
	vec3 dir = normalize(lookAt * ray_dir(FOV, uv));

	color = vec4(0.);

	vec3 result = raymarch(eye, dir, NEAR_D, FAR_D);
	float depth = result.x;
	float mat_idx = result.y;
	float iters = result.z;
	if (depth >= FAR_D) {
		color = vec4(0.);
		return;
	}

	vec3 p = eye + dir * depth; // recast the ray
	color = shade(p, mat_idx);

    // gamma
    color = vec4(pow(clamp(color.xyz, 0.0, 1.0), vec3(0.4545)), 1.0);
}