#version 330 core

const int TRACE_STEPS = 256; // reduced from 512 for performance
const float PI = 3.14159265;

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;

out vec4 FragColor;

vec3 modC(vec3 p, vec3 c){
    p = (fract(p / c + 0.5) - 0.5) * c;
    return p;
}
mat2 genRot(float v){
    return mat2(cos(v), -sin(v), sin(v), cos(v));
}
vec2 pMod(vec2 p, float c){
    p *= genRot(PI / c);
    float at = mod(atan(p.y, p.x), PI * 2.0 / c);
    float r = length(p);
    p = vec2(r * cos(at), r * sin(at));
    p *= genRot(-PI / c);
    return p;
}

vec2 path(float t){
    vec2 r;
    r.x = sin(t) + 0.5 * cos(t * 2.0);
    r.y = cos(t) + 0.5 * sin(t * 2.0);
    return r;
}

float mapSDF(vec3 p){
    p.xy += path(p.z + iTime);
    vec3 r = p;
    p.xy = pMod(p.xy, 12.0);
    p = modC(p, vec3(4.0,4.0,1.5));
    vec3 q = p;
    q.x -= 1.5;
    q = abs(q);
    float cube = max(q.x, max(q.y / 4.0, q.z)) - 0.25;
    float sp = length(p) - 0.3;
    float result = min(cube, sp);
    result = max(result, -(length(r.xy) - 0.5));
    return result;
}

float fog(float d){
    return 1.0 / (1.0 + d * d * 0.01);
}

vec3 getColor(vec3 p){
    vec3 c = sin(p);
    c = c * 0.5 + 0.5;
    return c;
}

vec3 traceVolume(vec3 o, vec3 r){
    vec3 volume = vec3(0.0);
    float l = 0.05;
    for(int i = 0; i < TRACE_STEPS; i++){
        vec3 p = o + r * float(i) * l;
        float d = mapSDF(p);
        vec3 c = getColor(p);
        float contrib = clamp(l - d, 0.0, l) * fog(float(i) * l);
        volume += contrib * c;
    }
    return volume;
}

vec3 cam(){
    vec3 c = vec3(0.0, 0.0, -1.5);
    c.z += iTime * 4.0;
    return c;
}
vec3 ray(vec2 uv, float z){
    vec3 r = normalize(vec3(uv, z));
    return r;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord){
    vec2 uv = (fragCoord.xy * 2.0 - iResolution.xy) / iResolution.y;
    vec3 o = cam();
    vec3 rd = ray(uv, 1.5);
    vec3 color = traceVolume(o, rd);
    fragColor = vec4(color, 1.0);
}

void main(){
    mainImage(FragColor, gl_FragCoord.xy);
    FragColor.a *= uAlpha;
}
