#version 330 core

out vec4 fragColor;

uniform vec2 _iResolution;
uniform uint _maxSteps;
uniform vec3 _camPose;
uniform vec3 _lookingAt;
uniform vec3 _camUp;
uniform float _fx;
uniform float _stopEpsilon;
uniform float _tmax;

uniform vec3 sdf_colors[{{sdfs|length}}];

{% for g in global_inputs -%}
uniform {{g}};
{% endfor %}

{% for sdf in sdfs %}
float map_{{sdf.name}}(in vec3 p)
{
    {% for line in sdf.lines -%}
    {{line}}
    {% endfor %}
}

vec3 calcNormal_{{sdf.name}}( in vec3 pos )
{
    vec2 e = vec2(1.0,-1.0);
    // const float eps = 0.0005;
    return normalize( e.xyy*map_{{sdf.name}}( pos + e.xyy*_stopEpsilon ) + 
					  e.yyx*map_{{sdf.name}}( pos + e.yyx*_stopEpsilon ) + 
					  e.yxy*map_{{sdf.name}}( pos + e.yxy*_stopEpsilon ) + 
					  e.xxx*map_{{sdf.name}}( pos + e.xxx*_stopEpsilon ) );
}
{% endfor %}

vec4 mainImage( in vec2 fragCoord )
{
    vec3 cam_norm = normalize(_lookingAt - _camPose);
    vec3 cam_right = normalize(cross(cam_norm, _camUp));
    vec3 cam_down = normalize(cross(cam_right, cam_norm));
    float fy = (_fx / _iResolution.x) * _iResolution.y;

    vec2 normalized_coord = (fragCoord / _iResolution) - 0.5;
    normalized_coord.x = normalized_coord.x * _fx;
    normalized_coord.y = normalized_coord.y * fy;

    vec3 p0 = cam_right * normalized_coord.x + cam_down * normalized_coord.y;
    p0 += _camPose;


    // raymarch
    int closest_object = -1;
    float closest_dist = _tmax;
    float t = 0.0;
    float h = 0.0;
    vec3 pos;
    {% for sdf in sdfs %}
    t = 0.0;
    for( uint i=0u; i<_maxSteps; i++ )
    {
        pos = p0 + t*cam_norm;
        h = map_{{sdf.name}}(pos);
        t += h;
        if (t>closest_dist) break;
        if( h<_stopEpsilon ) {
            closest_object = {{loop.index0}};
            closest_dist = t;
        }
    }
    {% endfor %}

    vec3 nor;
    switch(closest_object)
    {
        case -1:
            break;
        {% for sdf in sdfs %}
        case {{loop.index0}}:
            nor = calcNormal_{{sdf.name}}(p0 + closest_dist*cam_norm);
            break;
        {% endfor %}   
    }

    vec4 color = vec4(0.0);
    if( closest_object != -1 ) {
        color.w = 1.0;
        float dif = clamp( dot(nor,vec3(0.57703)), 0.0, 1.0 ) * 0.2;
        float amb = 1.4 + 0.3*dot(nor,vec3(0.0,1.0,0.0));
        // color.xyz = vec3(0.2,0.3,0.4)*amb + vec3(0.8,0.7,0.5)*dif;
        // color.xyz = vec3(0.2,0.3,0.4)*amb + vec3(0.8,0.7,0.5)*dif;
        color.xyz = clamp(sdf_colors[closest_object], 0.1, 0.9) * amb + vec3(0.8,0.7,0.5)*dif*0.2;
    }

    return color;
}

void main() {
    fragColor = mainImage(gl_FragCoord.xy);
}
