#version 450 core


// p.s. once again, THANK YOU 0B5VR FOR YOUR TDF21 SEMINAR!


// inputs 
in vec2 texCoord;

// outputs
layout (location = 0) out vec4 fragColor;

// textures
uniform sampler2D   t_bluenoise;
uniform samplerCube t_cubemap;

// uniforms
uniform float       g_time;
uniform ivec3       g_resolution;
uniform vec2        g_projectionParams;
uniform vec3        g_origin;
uniform vec3        g_target;
uniform mat4        m_proj;
uniform mat4        m_view;

// ------------------------
#define PI 3.141592653589793238
 
#define MAX_STEPS 128
#define MAX_DIST  256.
#define EPS_RAYMARCH .0001

mat2 rotate(float a) {return mat2(cos(a), -sin(a), sin(a), cos(a));}
vec3 rotateXYZ(vec3 o, vec3 a) {
  o.yz *= rotate(a.x);
  o.xz *= rotate(a.y);
  o.xy *= rotate(a.z);
 
  return o;
}

float rand(vec3 co){
    return 2*fract(sin(dot(co, vec3(12.9898, 78.233, 16.4495))) * 43758.5453)-.5;
}

float time = g_time + .005*rand(vec3(texCoord,g_time));

mat3 calcLookAtMatrix(vec3 origin, vec3 target, float roll) {
  vec3 rr = vec3(sin(roll), cos(roll), 0.0);
  vec3 ww = normalize(target - origin);
  vec3 uu = normalize(cross(ww, rr));
  vec3 vv = normalize(cross(uu, ww));
 
  return mat3(uu, vv, ww);
}
// domain repetition
 
float rep1(float p, float rep) {
    return float(mod(p+.5*rep, rep)-.5*rep);
}
 
vec2 rep2(vec2 p, vec2 rep) {
    return vec2(mod(p+.5*rep, rep)-.5*rep);
}
 
vec3 rep3(vec3 p, vec3 rep) {
    return vec3(mod(p+.5*rep, rep)-.5*rep);
}
 
// domain repetition - bounded
 
float repLim1(float p, float c, float l)
{
    return p-c*clamp(round(p/c),-l,l);
}
 
vec2 repLim2(vec2 p, vec2 c, vec2 l)
{
    return p-c*clamp(round(p/c),-l,l);
}
 
vec3 repLim3(vec3 p, vec3 c, vec3 l)
{
    return p-c*clamp(round(p/c),-l,l);
}

int material = 0;

vec2 sdSmoothBox( vec3 p, vec3 b, float r )
{
  vec3 q = abs(p) - b;
  return vec2(length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0) - r, 0.0);
}

vec2 sdPlane(vec3 p, vec3 n, float h) {
  return vec2(dot(p,n) + h, 1.);
}


// https://neort.io/product/bvcrf5s3p9f7gigeevf0
#define sabs(x) sqrt(x*x+1e-1)

vec2 mapTwister(vec3 po)
{
    float t = g_time;
    float sdf = MAX_DIST;
    for (int i = 0; i < 3; i++) {
        vec3 p = po;
	    p.xz += vec2(cos(1.2*t) + 1.2*sin(2.4*t+0.1*p.y), 2.1*sin(t*1.2) + 1.9*cos(1.4*t+0.5*p.y));
	    p.xz += 5.0*vec2(cos(i*(2 * PI / 3) + t*0.4), sin(i*(2 * PI / 3) + t*0.4));
	    vec3 pp = p;
	    pp.y = mod(abs(pp.y), 1.0);
	    pp.xz *= rotate(t*1.3 + p.y*0.2 + 1.4*sin(2.4*t + p.y*0.2));
	    sdf = min(sdf, sdSmoothBox(pp, vec3(vec2(1.0)*(1.0+.5*sin(p.x*0.3+t*1.3)), 1.0).xzy, 0.4).x);
    }
    
    return vec2(sdf, 2.0);
}

vec2 min2(vec2 a, vec2 b) {
	return a.x < b.x ? a : b;
}

vec2 map(vec3 p) {
  vec2 bl = mapTwister(p);
  vec2 pl = sdPlane(p, vec3(0,0,-1), 12);
  
  return min2(bl, pl);
}

vec3 calcNormal(vec3 p) {
    vec2 b = vec2(0., 0.0001);   // tweakme
    float a = mapTwister(p).x;
    return normalize(vec3(
                -a+mapTwister(p+b.yxx).x,
                -a+mapTwister(p+b.xyx).x,
                -a+mapTwister(p+b.xxy).x));
}

vec3 raymarch(vec3 o, vec3 d) {
  float t = 0, st = MAX_DIST;
  vec3 p = o;
  for (int i = 0; i < MAX_STEPS; i++) {
    p = o + d*t;
    float ct = map(p).x; st = min(st, abs(ct));
    if (abs(ct) < EPS_RAYMARCH || t > MAX_DIST) break;
    t += ct;
  }
  return vec3(t, st, map(p).y);
}

// hey 0b5vr =)
vec3 sin3(vec3 p, vec3 q) {return fract(vec3(sin(p.x*q.x), sin(p.y*q.y), sin(p.z*q.z)));}

vec3 poorManPBR(vec3 v, vec3 p, vec3 n) {
	float t = g_time;
	vec3 pp = vec3(p.y, p.xz*rotate(t*1.3 + p.y*0.2 + 1.4*sin(2.4*t + p.y*0.2))).yxz;
	float roughness = 0.05*(0.5*sin(20.0*(pp.x+pp.y+pp.z))*cos(40.*(n.x-n.y+n.z))+0.6);
	float metallic = 0.0;
	vec3  baseColor = 0.01+0.01*sin3(vec3(ivec3(p*vec3(1.,5.,1.))), vec3(88.4, 0.0, 889.4));	// tweakme
	vec3 V = v;
  vec3 L = normalize( vec3( 0.0, 0.0, -3.0 ) );
  vec3 N = n;
  vec3 H = normalize( L + V );
  
  float dotNL = max( dot( N, L ), 1E-3 );
  float dotNV = max( dot( N, V ), 1E-3 );
  float dotNH = max( dot( N, H ), 1E-3 );
  float dotVH = max( dot( V, H ), 1E-3 );

  vec3 irradiance = 32.0 * vec3( dotNL );
  
  float roughnessSq = roughness * roughness;
  
  // diffuse factor
  vec3 albedo = mix( baseColor, vec3( 0.0 ), metallic );
  vec3 diffuse = albedo / PI;
  
  // fresnel factor
  float F = max( 0.0, 1.0 - dotVH );
  F = pow( F, 5.0 );
  
  // GGX geometry factor
  float ggxv = dotNL * sqrt( mix( dotNV * dotNV, 1.0, roughnessSq ) );
  float ggxl = dotNV * sqrt( mix( dotNL * dotNL, 1.0, roughnessSq ) );
  float ggx = ggxv + ggxl;
  float Vis = ggx > 0.0 ? 0.5 / ggx : 0.0;
  
  // GGX distribution factor
  float f = dotNH * dotNH * ( roughnessSq - 1.0 ) + 1.0;
  float D = roughnessSq / ( PI * f * f );

  // specular factor
  vec3 f0 = mix( vec3( 0.04 ), baseColor, metallic );
  vec3 f90 = vec3( 1.0 );
  vec3 specular = vec3( D * Vis );
  
  return vec3(vec3(0.001) + irradiance * mix( albedo, specular, mix( f0, f90, F ) ));
}

vec3 doLighting(vec3 v, vec3 p) {
	vec3 n = calcNormal(p);	
	return vec3(poorManPBR(v, p, n));
}

void main()
{
    vec2 uv = (2.*(texCoord) - 1.) * vec2(float(g_resolution.x) / g_resolution.y, 1.); // uniform rendertarget coords

    vec3 ray = normalize(vec3(uv, 1.5));
    //ray = rotateXYZ(ray, vec3(0.6*cos(g_time*0.77), 0.8*sin(g_time*0.84), 0.8*sin(g_time*0.4)));
    vec3 origin = vec3(0, 0, -12);
    vec3 tt = raymarch(origin, ray);

    vec2 p = vec2(0.);
    vec3 color = vec3(0.0);
    vec3 pp = vec3(0.0);

    if (tt.x < MAX_DIST) {
      pp = origin + tt.x*ray;
      
      // todo: color calculation
      if (tt.z == 2.0) {
        color = doLighting(-ray, pp);
      }
      else {
        p = pp.xy * 0.02;
        p = abs(p*rotate(g_time*0.4));

        for (float i = 0.; i < 4.; i += 0.5) {
            p = p*rotate(g_time*0.3+i*.4);
            p -= .10-i*0.1;
            p = abs(.94*p+.01*i)-.08*(i/(.6+i));
        }
        p = mod(p, 2.)-1.;
        
        
        // palettes
        vec3 backdrop[] = vec3[](
                    1*vec3(0.870, 0.578, 0.133),
                    2*vec3(0.976, 0.462, 0.462),
                    8*vec3(0.96, 0.96, 0.96),
                    2*vec3(0.317, 0.262, 0.298),
                    1*vec3(0.717, 0.862, 0.838),
                    1*vec3(0.170, 0.378, 0.333),
                    3*vec3(0.976, 0.462, 0.162));
        
        float bdidx = (mod(length(p), .3) * 30.);
        vec3 bdcolor = mix(backdrop[int(bdidx)%7],backdrop[int(bdidx+1)%7], smoothstep(.45,.55,mod(bdidx,1.)));;
        
        color  = bdcolor;
      }
    }
    
    color = mix(vec3(1.0), color, clamp(2048.0/(tt.x*tt.x), 0., 1.));

    float depth = length(pp - origin);
	  fragColor = vec4(color, 1.0);
    float d = (m_proj[2][2]*depth - m_proj[3][2])/(-depth);
    gl_FragDepth = d;
}









