


///*
// Current code for: Affine Space / Automorphism
// Date: 2020 5/28 (idea/started before then around 5/7)
// Description: [url]https://twitter.com/C010011012/status/1270521006235377666[/url]
// [url]https://www.reddit.com/r/perfectloops/comments/h01vcj/aoc_impossible_cube_wave/[/url]
// Tags: voxel, penrose, swirledseries


//
// TODO:
// - wave start slow from left come faster
// - distributed/sharp shadows
// - interpolate cubes' normals slightly to that of spheres w/same center
// - to help w shading specular maps look nicer


// ↓ i know more
#define PI 3.14159265358979323846264338327950288419716939937510582097494459
#define TAU (2.*PI)

#define arg mod(float(iFrame)/60./4., 1.)



// sky box
// #define sund vec3(cos(TAU*arg), 1., -sin(TAU*arg))
#define sund normalize(vec3(.4, .5, -1.))

vec3 sky_map (vec3 p, vec3 d) {
    // return vec3(1.);
    // return vec3(0., 0., 100./255.);
    // return vec3(.5, .8, .9);
    float cloudamt2 = 0.;
    vec2 g = vec2(p.x-p.y, -p.z-p.y);
    g *= .08;
    cloudamt2 += .7*texture(iChannel0, vec2(g.x+g.y, g.x-g.y)/43.).r;
    cloudamt2 += .3*texture(iChannel0, vec2(g.x+g.y, g.x-g.y)/15.).r;
    cloudamt2 = pow(cloudamt2, 1.);
    return clamp(vec3(.5, .8, .9)*.2+cloudamt2, 0., 1.);
    // return vec3(.3, .5, .9)+vec3(1.)*cloudamt2;
    // return texture(iChannel2, d).rgb;
    float dimness = .5+.5*cos(d.y*PI);


    float a = 2.;
    float ah = dot(vec3(d.x, -abs(d.y), d.z), normalize(vec3(0., -a, 0.)) );
    float h = a/ah;
    float o = sqrt(h*h-a*a);
    float th = atan(d.z, d.x);
    // g is where it hits the ground/sky
    //vec2 g = vec2(cos(th)*o, sin(th)*o);

    // get ths sky color
    float cloudamt = 0.;
    cloudamt += .7*texture(iChannel0, vec2(g.x+g.y, g.x-g.y)/43.).r;
    cloudamt += .3*texture(iChannel0, vec2(g.x+g.y, g.x-g.y)/15.).r;
    cloudamt = pow(cloudamt, 2.);
    // cloudamt *= .4;
    // cloudamt += .0*texture(iChannel0, vec2(g.x+g.y, g.x-g.y)/90.).r;
    float sunamt = clamp(dot(d, sund), 0., 1.);
    float sunglow = pow(sunamt, 1.)*.2;

    vec3 SKY_COLOR = vec3(.0, .5, 1.);
    SKY_COLOR += cloudamt;
    // SKY_COLOR *= 1.-sunhalo;
    // SKY_COLOR += vec3(1., .9, .6)*sunhalo;
    SKY_COLOR += vec3(1., 1., 1.)*sunglow*2.;
    	dimness = mix(dimness, 1., sunglow);
    	// dimness = mix(dimness, 1., sunhalo);//sunamt*sunamt);
    if (d.y > .2) {
        return SKY_COLOR*pow(dimness, .05);
    }
    else {
        // return vec3(1.)*mod(floor(g.x)+floor(g.y), 2.);
        vec3 GROUND_COLOR = vec3(.3, .5, .3)*(.0+.8*texture(iChannel0, g/5.).r);

        GROUND_COLOR = 3.*GROUND_COLOR*GROUND_COLOR-2.*GROUND_COLOR*GROUND_COLOR*GROUND_COLOR;
        GROUND_COLOR *= 4.;
        GROUND_COLOR = clamp(GROUND_COLOR, 0., 1.);

        float puddleamt = .5+.5*sin(g.x*o*.05);
        puddleamt = .5*puddleamt+.5*texture(iChannel0, vec2(g.x, g.y)/20.).r;
        puddleamt = pow(puddleamt, .3);
        dimness = mix(dimness, 1., puddleamt)*.5;

        if (puddleamt > .8) {
            float amt = (puddleamt-.3)/.6;
            amt = clamp(amt, 0., 1.);
            amt = pow(amt, 2.);
            GROUND_COLOR = mix(GROUND_COLOR, SKY_COLOR+vec3(.4), amt);
        }
        if (-.2 <= d.y && d.y <= .2) {
            float amt = (d.y+.2)/.4;
            dimness = mix(
                dimness,
                mix(
                    dimness*2.,
                    1.,
                    -puddleamt
                ),
                amt
            );
            amt = 3.*amt*amt-2.*amt*amt*amt;
            amt = 3.*amt*amt-2.*amt*amt*amt;
            amt = 3.*amt*amt-2.*amt*amt*amt;
            GROUND_COLOR = mix(GROUND_COLOR, SKY_COLOR, amt);
        }
        return GROUND_COLOR*dimness*mix(0., 1., clamp(abs(d.y)*12., 0., 1.));

    }

}






// intersections have a normal and then a distance
#define inter_sky vec4(vec3(0.), 1e9)


// intersect plane
#define inter_plane_short(p, d, c, n) ( dot(-n, d) > 1e-6 ? vec4(n, dot(c-p, -n)/dot(-n, d)) : inter_sky )
// #define inter_plane_dist(p, d, c, n) ( dot(n, d) > 1e-6 ? dot(c-p, n)/dot(n, d) : inter_sky )
vec4 inter_plane (vec3 p, vec3 d, vec3 c, vec3 n) {
    vec4 inter_plane1;
    inter_plane1 = inter_plane_short(p, d, c, n);
    if (inter_plane1.w < inter_sky.w && inter_plane1.w > 0.) {
        return inter_plane1;
    }
    //inter_plane1 = inter_plane_short(p, d, c, -n);
    //if (inter_plane1.w < inter_sky.w && inter_plane1.w > 0.) {
    //    return inter_plane1;
    //}
    return inter_sky;
}


float h (vec2 p) {
    float amt = clamp(arg*10.-max(p.x+9., 0.)*0.4, 0., 1.);
    //float amt = clamp(arg*10.-max(
    //    length(vec2(p.x-1., -p.y-1.0))*3./2.,
    //    0.
    //)*0.5, 0., 1.);
    amt = -amt*(amt-1.)*1.0+amt;
    return -amt*2.;
}

// intersect custom SDF
// #define SDF(p) (length(p)-2.)
//#define SDF(p) (length(max(abs(p-vec3(.5))-.3, 0.))-.18)
//#define SDF(p) (length(max(abs(p-vec3(.5))-.4, 0.))-.07)
#define SDF(p) (length(vec3(\
    max(abs((p).x-.5)-.4, 0.),\
    max(((p).y-.5)-.4, 0.),\
    max(abs((p).z-.5)-.4, 0.)\
))-.07)

#define inf vec2(1e-3, 0.)
vec4 inter_SDF (vec3 p, vec3 d) {
    vec3 camp = p;
    float SDFp;
    for (int i=0; i<200; ++i) {
        SDFp = SDF(p);
        if (abs(p.x-.5) > 1. || abs(p.z-.5) > 1. || SDFp > 4.) {
            break;
        }
        else if (abs(SDFp) < 1e-3) {
            return vec4(
                normalize(-SDFp+vec3(
                    SDF(p+inf.xyy),
                    SDF(p+inf.yxy),
                    SDF(p+inf.yyx)
                )),
                length(p-camp)
            );
        }
        p += d*abs(SDFp)*.9;
    }
    return inter_sky;
}




vec4 inter_pillars (vec3 p, vec3 d) {
    vec4 inter_closest;
    vec4 inter_current;
    vec3 camp = p;
    for (int jumps=0; jumps<10; ++jumps) {
        vec4 inter_solid1 = inter_SDF(vec3(fract(p.x), p.y+h(floor(p.xz)), fract(p.z)), d);
        //vec4 inter_solid1 = inter_SDF(p, d);
        if (inter_solid1.w < inter_sky.w) {
            // hits sdf - portals will not be hit
            inter_solid1.w += length(p-camp);
            return inter_solid1;
        }
        // check 4 "panes" / portals
        inter_closest = inter_sky;
        inter_current = inter_plane(p, d, floor(p)+vec3(1., 0., 0.), normalize(vec3(-1., 0., 0.)) );
        if (inter_current.w < inter_closest.w) {
            inter_closest = inter_current;
        }
        inter_current = inter_plane(p, d, floor(p)+vec3(0., 0., 1.), normalize(vec3(0., 0., -1.)) );
        if (inter_current.w < inter_closest.w) {
            inter_closest = inter_current;
        }
        inter_current = inter_plane(p, d, floor(p)+vec3(0., 0., 0.), normalize(vec3(1., 0., 0.)) );
        if (inter_current.w < inter_closest.w) {
            inter_closest = inter_current;
        }
        inter_current = inter_plane(p, d, floor(p)+vec3(0., 0., 0.), normalize(vec3(0., 0., 1.)) );
        if (inter_current.w < inter_closest.w) {
            inter_closest = inter_current;
        }
        p += d*inter_closest.w;
        p += d*1e-3;
    }
    return inter_sky;
}




// main image - camera/initial raycast, setting pixel color


void mainImage (out vec4 fragColor, in vec2 fragCoord) {
    vec3 retina = vec3(0.);
    #define numsamples 1.
    vec4 randy;
    for (float samplei=0.; samplei<numsamples; ++samplei) {
        randy = texture(iChannel1, vec2(
            samplei/256.+fragCoord.x/256., float(iFrame)/256.+fragCoord.y/256.
        )).rgba;
        // -1 to 1 in y, proportional in x
        vec2 screen = (
            -iResolution.xy/2.
            +fragCoord+randy.rg
        )/iResolution.y*2.;

        // camera
        vec3 camp = vec3(screen.xy*4., -20.);
        vec3 camd = normalize(vec3(0., 0., 1.));

        // float ph = (iMouse.y/iResolution.y*2.-1.)*4.;
        #define ph atan(1./sqrt(2.))
        mat2 zenith = mat2(
            cos(ph), cos(PI*.5+ph),
            sin(ph), sin(PI*.5+ph)
        );
        camp.yz *= zenith;
        camd.yz *= zenith;

        // float th = (iMouse.x/iResolution.x*2.-1.)*TAU;
        #define th (PI*.25)
        mat2 azimuth = mat2(
            cos(th), cos(PI*.5+th),
            sin(th), sin(PI*.5+th)
        );
        camp.xz *= azimuth;
        camd.xz *= azimuth;

        camd = normalize(camd);


        // space skipping
        vec4 skp = inter_plane(camp, camd, vec3(0., 4., 0.), normalize(vec3(0., 1., 0.)) );
        if (skp.w < inter_sky.w && skp.w > 0.) {
            camp += camd*skp.w;
        }



        // raytracing
        vec3 finalCol = vec3(0.);
        float shiny = 1.;
        vec3 p = camp;
        vec3 d = camd;

        for (int bounces=0; bounces<32; ++bounces) {

            if (shiny < .1) {
                break;
            }

            vec4 inter_closest = inter_sky;
            // for (var i in scene... inter_current = scene[i]) {
                vec4 inter_current = inter_pillars(p, d);
                if (inter_current.w < inter_closest.w) {
                    inter_closest = inter_current;
                }
            //}

            if (dot(d, normalize(vec3(0., 1., 0.))) > 0. && p.y > 5.) {
                // far enough from floor and angled away from such that
                // ray will garunteed hit sky
                // finalCol += vec3(1., 0., 0.)*shiny;
                // shiny *= 0.;
                break;
            }
            if (inter_closest.w >= inter_sky.w) {
                // hits sky - stop reflection/refraction loop
                break;
            }
            else {
                // hits object
                p += d*inter_closest.w;

                float czk = mod(floor(p.x)+floor(p.z), 2.);

                // shadows
                vec4 shado = inter_pillars(p+sund*2e-3, sund);
                //vec4 shado = inter_sky;
                if (shado.w < inter_sky.w) {
                    #define shininess .6
                    finalCol += shiny*shininess*vec3(0.);
                    shiny *= 1.-shininess;
                    #undef shininess
                }

                if (czk < .5){
                    // have normal and distance to closest itnersection
                    // reflect, refract, texture, etc.
                    #define shininess .5
                    vec3 TEXp = vec3(1.)*mod(floor(p.x*7.)+floor(p.z*7.), 2.);
                    TEXp = vec3(0.);
                    //TEXp.rg = vec2(
                    //    mod((p.x-p.y)*.025, 1.),
                    //    mod((-p.z-p.y)*.025, 1.)
                    //)*4.;
                    finalCol += TEXp*shiny*shininess;
                    shiny *= 1.-shininess;
                    d = reflect(d, inter_closest.xyz);
                    p += d*1e-3;
                    #undef shininess
                }
                else {

                    #define shininess .5
                    // also .2
                    vec3 TEXp = abs(inter_closest.xyz);
                    TEXp.rgb  = vec3(
                        min(min(TEXp.r, TEXp.g), TEXp.b),
                        TEXp.r,
                        max(max(TEXp.r, TEXp.g), TEXp.b)
                    );
                    //TEXp = vec3(.0, .2, .6);
                    TEXp = vec3(0., .7, .7);
                    finalCol += TEXp*shiny*shininess;
                    shiny *= 1.-shininess;
                    d = reflect(d, inter_closest.xyz);
                    p += d*1e-3;
                    #undef shininess
                }

            }
        }

        if (shiny == 1.) {
            // never hit object - real sky color
            finalCol += shiny*vec3(0., 1., 100./255.);
            shiny *= 0.;
        }
        // add sky color to final reflection/refraction
        // finalCol += (vec3(0., 0., 100./255.))*shiny;
        finalCol += sky_map(p, d)*shiny;
        retina += finalCol;
    }
    fragColor = vec4(retina/numsamples, 1.);
}

/**/















/*

// Old Code for: raymarched reflections!
// Date: 2019 10/22 (around then)
// Tags: raymarching
// Description: Coded by me from scratch, with refrence and
// inspiration from Shanes shader and CodeParade's video, as well as
// TheArtOfCode's raymarching tutorial


// MADE ENTIRELY FROM ME BY SCRATCH FROM TJE START
// I ONLY COPIED THE FOV USED IN TheArtOfCodes Ray/Sphere intersection tutorial
// and technique described in this super awesome video by
// CodeParade https://www.youtube.com/watch?v=svLzmFuSBhk

// "i swear im trying so hard but my raytracers reflections were
//  glitching and i couldnt figure out the phong lighting model ;("
// - me before i made THIS

// some help from
// https://www.shadertoy.com/view/4dt3zn
// https://www.youtube.com/watch?v=svLzmFuSBhk
// https://www.youtube.com/watch?v=PGtv-dBi2wE

const int LightSourceT_point = 0;
const int LightSourceT_ray = 1;

struct LightSource {
    int type;
    vec3 p;
    vec3 col;
};

struct Ray {
    vec3 origin;
    vec3 direction;
};

float SDF(vec3 p) {
    float boxrad = 3.;
    p = mod(p, boxrad)-boxrad/2.;
    return sqrt(p.x*p.x/8.+p.y*p.y*2.+p.z*p.z/8.)-.5;
    // return length(p)-1.5;
    // return length(p-vec3(1, 1, 2))-1.;
}

vec3 reflekt(vec3 p) {
    float inf = 1e-3;
    return normalize(
        vec3(
            SDF(vec3(p.x+inf, p.y, p.z)),
            SDF(vec3(p.x, p.y+inf, p.z)),
            SDF(vec3(p.x, p.y, p.z+inf))
        )-
        vec3(SDF(p))
    );
}

vec3 colour(vec3 p, LightSource[3] lights) {
    vec3 light = vec3(0., 0., 0.);
    for (int i=0; i<lights.length(); ++i) {
        vec3 dir = vec3(0.);
        if (lights[i].type == LightSourceT_point) {
            dir = normalize(lights[i].p-p);
        }
        else if (lights[i].type == LightSourceT_ray) {
        	dir = normalize(lights[i].p);
        }
        light.x += dot(dir, reflekt(p))*lights[i].col.x;
        light.y += dot(dir, reflekt(p))*lights[i].col.y;
        light.z += dot(dir, reflekt(p))*lights[i].col.z;
    }
    light = clamp(light, 0., 1.);
    return vec3(light);
}

vec3 march(Ray ray) {
    LightSource[3] lights;
    lights[0] = LightSource(LightSourceT_ray, normalize(vec3(1, 2, 0)), vec3(1., 0., 0.));
    lights[1] = LightSource(LightSourceT_ray, normalize(vec3(2, 0, 0)), vec3(0., 1., 1.));
    vec3 col = vec3(0.);
    float rcount = 0.;
    float rindex = .35;
    float rmax = 6.;
    int dmax = 108;
    for (int i=0; i<dmax && rcount<rmax; ++i) {
        float DE = SDF(ray.origin);
        if (DE <= 1e-2) {
            vec3 col2 = colour(ray.origin, lights);
            if (rcount == rmax-1.) {
                col2 = vec3(0., 0., 100./255.);
            }
            ray.direction = reflect(ray.direction, reflekt(ray.origin));
        	ray.origin = ray.origin + ray.direction*1e-2;
            if (true) {
                float rindex2 = pow(rindex, rcount);
                // col2 = vec3(0);
                col = col*(1.-rindex2)+col2*rindex2;
                // col = col2;
			}
            ++rcount;
        }
        // if it diverges to far return te background color
        if (i == dmax-1 && rcount > 0.) {
            float rindex2 = pow(rindex, rcount);
            vec3 col2 = vec3(0., 0., 100./255.);
            col = col*(1.-rindex2)+col2*rindex2;
        }
        ray.origin = ray.origin + ray.direction*DE;
    }
    if (rcount > 0.) {
        return col;
    }
    return vec3(0., 0., 100./255.);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    vec2 uv = (fragCoord-iResolution.xy/2.)/iResolution.x*2.;
    vec3 dir = vec3(uv.x, uv.y, 1.);
    float mag = sqrt(dir.z*dir.z+dir.x*dir.x);
    float ang = atan(dir.z, dir.x)+(iMouse.x+150.-iResolution.x/2.)/90.;
    dir = vec3(cos(ang)*mag, dir.y, sin(ang)*mag);
    float pi = 3.1415926535897;
    Ray ray = Ray(vec3(0.-(iTime+sin(iTime))/pi*2.*2., 0., 0.), normalize(dir));

    vec3 retina = march(ray);

    // Output to screen
    fragColor = vec4(retina, 1.0);
}
/**/