#define R (iResolution.xy)
#ifndef HW_PERFORMANCE
#define iTime (0.0)
#define iFrame (0)
uniform sampler2D iChannel3;
#define iResolution vec2(0.0)
#define iMouse vec4(0.0)
#endif
#define T (iTime)
#define M_PI (3.14159265359)
#define TAU (M_PI * 2.0)
#define ZERO (min(0, int(iTime)))
#define adot(a,b) (abs(dot(a, b)))
#define cdot(a,b) (max(0.0, dot(a, b)))
#define rgb(a,b,c) (vec3(a, b, c) / 255.0)

#define FORWARD_DECL\
    float sdf(inout Data data, in vec3 p) { return FAR; }\
    vec3 getAlbedo(inout Data data) { return vec3(0.0); }\
    vec3 getSky(in vec3 rd) { return vec3(0.0); }

#define SAMPLE(var, OBJ, id_) if (id_ != skip && var < dist) { id = id_; dist = var; o = OBJ; }
// Ray marcher constants

#define NEAR 0.003
#define FAR 90.0
#define STEPS 60
#define SHADOW_STEPS 33
#define FAR_LOD 1.0
#define NORMAL_RANGE 0.001
#define AMBIENT 0.25
#define NUM_LIGHTS 1
#define ID_NONE -1

// Colors

#define COLOR_SKY vec3(0.29, 0.61, 0.9)
#define COLOR_SUN vec3(0.97, 0.81, 0.79)

// Render constants

#define SPECULAR_AMPLITUDE 64.0
#define SHADOW_NDOTL_EPSILON 0.000003

// Default sun light

#define LIGHT_SUN Light(vec3(1, 2, -3), vec3(0.0), COLOR_SUN, 2.0, LIGHT_AMBIENT)

struct Object {
    vec3 p;
    vec3 q;
};

#define NEW_OBJECT Object(vec3(0.0), vec3(0.0))

struct Material { float rough; float spec; float metallic; float lum; float z; float ior; };
#define NEW_MATERIAL Material(1.0, 1.0, 0.0, 0.0, 0.0, 0.0)

struct Data {
    vec3 q;
    vec3 ro;
    vec3 rd;
    vec3 p;
    vec3 n;
    float d;
    int skip;
    int id;
    float sig;
    Material m;
    Object o;
};
#define NEW_DATA Data(vec3(0.0), vec3(0.0),vec3(0.0), vec3(0.0), vec3(0.0), FAR, ID_NONE, ID_NONE, 1., NEW_MATERIAL, NEW_OBJECT)


struct Ray {
    vec3 ro;
    vec3 rd;
};

#ifndef HW_PERFORMANCE
float sdf(inout Data data, in vec3 p) { return FAR; }
vec3 getAlbedo(inout Data data) { return vec3(0.0); }
vec3 getSky(in vec3 rd) { return vec3(0.0); }
#else
float sdf(inout Data data, in vec3 p);
vec3 getAlbedo(inout Data data);
vec3 getSky(in vec3 rd);
#endif


// Utilities

vec3 mix4(vec3 c1, vec3 c2, vec3 c3, vec3 c4, vec3 m) {
    return mix(mix(mix(c1, c2, m.x), c3, m.y), c4, m.z);
}

float smin(float a, float b, float k) {
    float h = clamp(0.5 + 0.5*(a-b)/k, 0.0, 1.0);
    return mix(a, b, h) - k*h*(1.0-h);
}

float smax(float a, float b, float k) {
    return smin(a, b, -k);
}

vec3 smin(vec3 a, vec3 b, float k) {
    vec3 h = clamp(0.5 + 0.5*(a-b)/k, 0.0, 1.0);
    return mix(a, b, h) - k*h*(1.0-h);
}

vec3 smax(vec3 a, vec3 b, float k) {
    return smin(a, b, -k);
}

float sclamp(in float v, in float mi, in float ma, in float k) {
    return smin(smax(v, mi, k), ma, k);
}

vec3 makeNormal(in vec3 wn, in float z, in float mx, in float s) {
    if (abs(z) <= 0.0000001) return wn;
    vec2 dx = vec2(dFdx(z), 0.0001);
    vec2 dy = vec2(0.0001, dFdy(z));
    float bumpScale = s;
    vec3 cp = cross(vec3(dx, bumpScale), vec3(dy, bumpScale));
    if (length(cp) <= 0.00002) return wn;
    cp = normalize(cp);
    mx *= max(0.0, 1.0 - max(0.0, dot(cp, -wn)));
    vec3 next = normalize(mix(wn, normalize(wn+cp), mx));
    if (length(next) <= 0.0001) return wn;
    return next;
}

mat2 rot(in float a) { float c = cos(a); float s = sin(a); return mat2(c, s, -s, c); }

vec3 look(in vec2 uv, in vec3 point, in vec3 ro, in float zoom) {
    vec3 camForward = normalize(point - ro);
    vec3 camRight = -normalize(cross(camForward, vec3(0, 1, 0)));
    vec3 camUp = -normalize(cross(camRight, camForward));
    vec3 screenCenter = ro + camForward * zoom;
    vec3 screenIntersection = screenCenter + uv.x * camRight + uv.y * camUp;
    return normalize(screenIntersection - ro);
}

vec3 pointRot(in vec3 p, in vec3 r) {
    p.yz *= rot(r.x);
    p.xz *= rot(r.y);
    return p;
}

// UV functions

vec2 boxUv(in vec3 p, in vec3 n) {
    return mix(mix(p.xy, p.yz, round(adot(n, vec3(1, 0, 0)))), p.xz, round(adot(n, vec3(0, 1, 0))));
}

vec2 sphereUv(in vec3 p) {
    float u = acos(p.y);
    float v = atan(p.x, p.z);
    u = 0.5 - u / M_PI;
    v = 0.5 + v / M_PI;
    return vec2(u, v);
}

// Distance functions

float line2D(in vec2 p, in vec2 a, in vec2 b, in float t) {
    vec2 pa = p - a;
    vec2 ba = b - a;
    float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
    return clamp(01.0-length(pa - ba * h)/t, 0.0, 1.0);
}

float sphereSDF(in vec3 p, in float r) {
    return length(p) - r;
}

float boxSDF(in vec3 p, in vec3 s) {
    p = abs(p) - s;
    return length(max(p, 0.0)) + min(max(p.x, max(p.y, p.z)), 0.0);
}

float lineSDF(in vec3 p, in vec3 a, in vec3 b, in float r) {
    vec3 pa = p - a;
    vec3 ba = b - a;
    return length(pa - ba * (clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0))) - r;
}

float cylSDF(in vec3 p, in vec3 a, in vec3 b, in float r) {
    vec3 ab = b - a;
    vec3 ap = p - a;
    float t = dot(ab, ap) / dot(ab, ab);
    vec3 c = a + t * ab;
    float x = length(p - c) - r;
    float y = (abs(t - 0.5) - 0.5) * length(ab);
    float e = length(max(vec2(x, y), 0.0));
    float i = min(max(x, y), 0.0);
    return e + i;
}

float torusSDF(in vec3 p, in float r1, float r2) {
    p = p.yzx;
    float ll = length(p.yz)-r1;
    return length(vec2(ll,p.x))-r2;
}

// Noise functions

uint hash21u(in vec2 ip, in float seed) {
    uvec2 p = uvec2(floatBitsToUint(ip.x), floatBitsToUint(ip.y));
    uint s = floatBitsToUint(seed);
    s ^= ~s >> 3U;
    p ^= (p << 17U);
    s ^= (~p.x);
    s ^= (~p.y);
    p ^= (p >> 11U);
    p ^= (p << 5U);
    p ^= (s << 3U);
    return ((p.x + p.y) ^ (p.x * s + p.y))*293U;
}

float hash21(in vec2 ip, in float seed) { return float(hash21u(ip, seed)) / float(0xFFFFFFFFU); }

vec3 hash23(in vec2 ip, in float seed) {
    uint n = hash21u(ip, seed);
    n ^= (n >> 13U);
    return vec3(float((n >> 16U) & 0xFFU), float((n >> 8U) & 0xFFU), float(n & 0xFFU)) / float(0xFFU);
}

float hash21Fast(in vec2 p, in float seed) {
    float ma = 64.092391;
    float x = mod(p.x*4.0291, ma) / ma;
    float y = mod(p.y*5.5920, ma) / ma;
    vec2 p2 = mix(vec2(x,y), vec2(y, x), fract(seed*10.29891231))*2.0-1.0;
    float r1 = mod(100.2891382*fract(seed+(dot(p, p2)*16.2099123)), ma)/ma;
    float r2 = fract((p.x*3.392912+p.y)*distance(p2, p-seed));
    return fract(dot(p, vec2(r1, r2)));
}

float noise(in vec2 p, in float seed) {
    vec2 id = floor(p);
    vec2 lv = fract(p);
    lv = lv*lv*(3.0-2.0*lv);
    return mix(
        mix(hash21(id, seed), hash21(id+vec2(1,0), seed), lv.x),
        mix(hash21(id+vec2(0, 1), seed), hash21(id+vec2(1,1), seed), lv.x),
        lv.y
    );
}

#define NOISE(p, seed, lod) (textureLod(iChannel3, (p + (seed*1.9287827185))/256.0, lod).rgb)

vec3 snoise(in vec2 p, in float seed, in float lod) {
    p += 0.99288124;
    vec2 id = floor(p);
    vec2 lv = fract(p); lv = lv*lv*(3.0-2.0*lv);
    return mix(
        mix(NOISE(id, seed, lod), NOISE(id + vec2(1, 0), seed, lod), lv.x),
        mix(NOISE(id + vec2(0, 1), seed, lod), NOISE(id + vec2(1, 1), seed, lod), lv.x),
        lv.y
    );
}

vec3 snoise(in vec2 p, in float seed, in float lod, in float freq, const in int octaves) {
    p += 0.99288124;
    float div = 0.0;
    float amp = 1.0;
    vec3 n = vec3(0.0);
    for (int i = ZERO; i < octaves; i++) {
        n += amp * snoise(p*freq, seed, lod); div += amp; amp /= 2.0; lod /= 2.0; freq *= 2.0;
    }
    return n / div;
}

vec3 snoiseWarp(in vec2 p, in float seed, in float lod, in float freq, const in int octaves, in float warp) {
    p += 0.99288124;
    float div = 0.0;
    float amp = 1.0;
    vec3 n = vec3(0.0);
    for (int i = ZERO; i < octaves; i++) {
        n += amp * snoise(p*freq, seed, lod); div += amp; amp /= 2.0; lod /= 2.0; freq *= 2.0;
        p += (n.xy*n.z)*warp;
    }
    return n / div;
}

vec3 sineMut(in vec2 p, in float seed) {
    vec3 r = vec3(0.001);
    for (int i = ZERO; i < 4; i++) {
        r += vec3(cos(p.x+r.x), sin(p.y+r.y), sin((r.z+seed)*6.28));
        p *= mat2(6.28, 8.0, -6.28, 8.0)*0.1;
       // r.xz *= mat2(6.28, 8.0, -6.28, 8.0)*0.1;
    }
    
    float l = dFdx(length(p-r.xy+r.z));
    return clamp((r*l)/2., 0.0, 1.0);
}

vec3 sineNoise(in vec2 p, in float seed, in float freq) {
    float div = 0.0;
    float amp = 1.0;
    vec3 n = vec3(0.0);
    
    for (int i = ZERO; i < 4; i++) {
        n += amp*sineMut(p*freq, seed); div += amp; amp /= 2.; freq *= 2.0;
        p *= mat2(6.28, 8.0, -6.28, 8.0)*0.1;
    }
    
    return clamp(n/div, 0.0, 1.0);
}


vec3 sineMut2(in vec2 p, in float seed) {
    vec2 s1 = vec2(sin(p.x+seed), cos(p.y+seed));
    float r1 = dot(s1, p);
    s1 *= rot(r1);
    vec2 s2 = vec2(cos(r1+seed), sin(r1+seed));
    float r2 = dot(s2, p);
    s2 *= rot(r2);
    
    vec3 v = vec3(s1.x, s1.y, s2.x);
    v.xz *= rot(s2.y);
    return exp(v-1.);
}

vec3 sineNoise2(in vec2 p, in float seed, in float freq) {
    float div = 0.0;
    float amp = 1.0;
    vec3 n = vec3(0.0);
    
    for (int i = ZERO; i < 4; i++) {
        n += amp*sineMut2(p*freq, seed); div += amp; amp /= 2.; freq *= 2.0;
        p += (n.xz*n.y)*amp;
    }
    
    return n/div;
}

// Shading & Colors

float luma(vec3 color) { return dot(color, vec3(0.299, 0.587, 0.114)); }

vec3 aces(vec3 x) {return x*(2.51*x + .03) / (x*(2.43*x + .59) + .14); }

vec3 fresnelSchlickRoughness(float NdotV, vec3 F0, float rough) {
  return F0 + (max(vec3(1.0 - rough), F0) - F0) * pow(max(1.0 - NdotV, 0.0), 5.0);
}

vec3 fresnelSchlick(vec3 F0, float cosTheta) {
	return F0 + (vec3(1.0) - F0) * pow(1.0 - cosTheta, 5.0);
}

float ndfGGX(float cosLh, float roughness) {
	float alpha   = roughness * roughness;
	float alphaSq = alpha * alpha;

	float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
	return alphaSq / (M_PI * denom * denom);
}

float gaSchlickG1(float cosTheta, float k) {
	return cosTheta / (cosTheta * (1.0 - k) + k);
}

float gaSchlickGGX(float cosLi, float cosLo, float roughness) {
	float r = roughness + 1.0;
	float k = (r * r) / 8.0;
	return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k);
}

vec3 EnvBRDFApprox(vec3 specularColor, float roughness, float ndotv) {
    const vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022);
    const vec4 c1 = vec4(1, 0.0425, 1.04, -0.04);
    vec4 r = roughness * c0 + c1;
    float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y;
    vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
    return specularColor * AB.x + AB.y;
}

// Marching

bool march(inout Data data, in vec3 ro, in vec3 rd) {
    float d = 0.0;
    float sig = data.sig;
    for (int i = ZERO; i < STEPS; i++) {
        vec3 p = ro+rd*d;
        float next = sig*sdf(data, p);
        d += next;
        if (abs(next) <= (NEAR * (1.0 + (d / FAR_LOD)))) break;
        if (abs(d) >= FAR) return false;
    }
    
    Data tmp = NEW_DATA;
    vec3 p = ro+rd*d;
    vec2 e = vec2(NORMAL_RANGE, 0.0);
    vec3 n = normalize(sdf(tmp, p) - vec3(
        sdf(tmp, p - e.xyy),
        sdf(tmp, p - e.yxy),
        sdf(tmp, p - e.yyx)
    ));
    
    data.d = d;
    data.p = p;
    data.n = n;
    
    return true;
}

// Modified version of iq's https://www.shadertoy.com/view/lsKcDD
float getShadow(in vec3 ro, in vec3 rd, in vec3 n, in float near, in float far, in int skip) {
    Data data = NEW_DATA;
    float NdotL = dot(n, rd);
    float res = 1.0;
    float ep = 1e20;
    float t = NEAR;
    float ss = (far/FAR);
    float bb = 1.0;
    for(int i = ZERO; i < SHADOW_STEPS && t < far; i++){
        vec3 p = ro + rd*t;
        float j = float(i);
        
        // Move point away from surface
        // This solves some ugly banding issues
        p += n*clamp(pow(dot(normalize(rd+p), n), 2.)*3., 0.0, 0.93); 
        
        float h = sdf(data, p);
        
        float y = h*h / (2.9 * ep);
        float d = sqrt(h*h-y*y);
        res = min(res, d/(ss*max(0.0, t-y)));
        bb = max(0.0, min(bb, float(data.id == 4)/d/(ss*max(0.0, t-y))));
        ep = h;
        t += h;
    }
    
    // Fixes issues with sharp corners & edges
    float ra = far*0.5; // radius
    float fe = ra*0.77; // feather
    res = max(res, 0.);
    res = max(mix(res*res,
        (1.0-smoothstep(ra-fe, ra+fe, far-t)),
        smoothstep(0.0, 0.6, res+smoothstep(0.0, 0.9, t / (1.0+t*6.)))
    ), 0.);
    res += clamp(0.25*AMBIENT*max(0.02, t/far), 0.001, AMBIENT);
    //////////////////////////////////////////
    
    return clamp(res+(bb*0.35), AMBIENT, 1.);
}


// Lighting

struct Light { vec3 p; vec3 d; vec3 c; float s; int type; };
#define LIGHT_AMBIENT 0
#define LIGHT_POINT 1
#define LIGHT_DIR 2

vec3 getLightDir(in Light light, in vec3 p) {
    return light.type == LIGHT_AMBIENT ? normalize(light.p) :
           light.type == LIGHT_POINT ? normalize(light.p - p) :
           normalize(light.d);
}

vec3 getLightAtt(in Light light, in vec3 p, in vec3 n) {
    vec3 L = getLightDir(light, p);
    float NdotL = max(AMBIENT, dot(n, L));
    
    switch (light.type) {
        case LIGHT_AMBIENT: return light.c * light.s * NdotL; break;
        case LIGHT_POINT: return light.c * NdotL * (
            pow(light.s, 2.0) / max(0.0001, pow(distance(light.p, p), 2.0))
        ); break;
        case LIGHT_DIR: return light.c * light.s * NdotL * smoothstep(0.8, 1.0, dot(
            normalize(light.p - p),
            L
        )); break;
    }
    
    return vec3(0.0);
}

vec3 forEachLight(in Light light, in Data data, in vec3 ro, in vec3 rd, in vec3 diffuse) {
    vec3 p = data.p;
    vec3 n = data.n;
    vec3 L = getLightDir(light, p);
    vec3 ref = reflect(L, n);
    float VdotR = cdot(rd, ref);
    float spec = data.m.spec * pow(VdotR, SPECULAR_AMPLITUDE);
    vec3 att = getLightAtt(light, p, n);
    float shadow = getShadow(p + (n*NEAR*2.0), L, n, data.d, light.type == LIGHT_AMBIENT ? FAR :
        distance(light.p, p), data.id);
        
    float metallic = data.m.metallic;
    float rough = data.m.rough;
    float specF = data.m.spec;
    
    vec3 LH = normalize(L + -rd);
    float cosLi = max(0.0001, dot(n, L));
	float cosLh = max(0.0001, dot(n, LH));
    float cosLo = max(0.0001, dot(n, -rd));
    vec3 F0 = mix(vec3(0.04), diffuse, metallic);
    vec3 F  = fresnelSchlick(F0, max(0.0, dot(LH, -rd)));
    float D = ndfGGX(cosLh, rough);
    float G = gaSchlickGGX(cosLi, cosLo, rough);
    vec3 kd = mix(vec3(1.0) - F, vec3(0.0), metallic);
    vec3 diffuseBRDF = kd * diffuse;
    vec3 specularBRDF = (F * D * G) / max(0.00003, 4.0 * cosLi * cosLo);
    
        
    return (diffuseBRDF + specF*specularBRDF) * att * shadow; 
}

bool rayGetColor(inout Data data, in Light lights[NUM_LIGHTS],
    in bool with_sky, inout vec3 col, in vec3 ro, in vec3 rd, inout vec3 diffuse) {
    if (!march(data, ro, rd)) {
        if (with_sky) {
            col += getSky(rd);
        }
        return false;
    }
    vec3 albedo = getAlbedo(data);
    diffuse = albedo / M_PI;
    for (int i = ZERO; i < NUM_LIGHTS; i++) {
        Light light = lights[i];
        col += forEachLight(light, data, ro, rd, diffuse);
    }
    col = max(col, 0.0);
    return true;
}

bool raycast(
    inout Data data,
    in Light lights[NUM_LIGHTS],
    inout vec3 col,
    in vec3 ro,
    in vec3 rd
 ) {
    bool withSky = true;
    float dist = FAR;
    for (int i = ZERO; i < 3; i++) {
        vec3 diffuse = vec3(0.0);
        if (!rayGetColor(data, lights, withSky, col, ro, rd, diffuse)) return i <= 0 ? false : true;
        if (i <= 0) { dist = data.d; }
        
        float rough = data.m.rough;
        float metallic = data.m.metallic;
        float ior = data.m.ior;
        vec3 n = data.n;
        vec3 p = data.p;

        if (rough < 0.993) {
            vec3 F0 = mix(vec3(0.04), diffuse, metallic);
            float NdotV = max(0.0, dot(n, -rd));
            vec3 F = fresnelSchlickRoughness(NdotV, F0, rough);
            
            if (length(F) > 0.01) { 
                Data reflectData = NEW_DATA;
                reflectData.sig = data.sig;
                vec3 reflectRo = p + (n*NEAR*3.);
                vec3 reflectDir = reflect(rd, n);
                vec3 reflectDiffuse = vec3(0.0);
                vec3 reflectColor = vec3(0.0);
                vec3 env = EnvBRDFApprox(mix(vec3(0.77), diffuse, metallic*metallic*0.15), rough*rough, NdotV);
                reflectData.skip = data.id;
                rayGetColor(reflectData, lights, true, reflectColor, reflectRo, reflectDir, reflectDiffuse);
                col += reflectColor * (F * env);
            }
        }

        if (ior > 0.00019) {
            vec3 enterDir = refract(rd, n, 1.0/ior);
            
            if (length(enterDir) <= 0.00003) {
                break;
            }
            
            Data enterData = NEW_DATA;
            enterData.skip = data.skip;
            enterData.sig = -data.sig;
            
            vec3 enterP = p+(n*enterData.sig*NEAR*16.);
            vec3 refractDiffuse = vec3(0.0);

            rayGetColor(enterData, lights, false, col, enterP, enterDir, refractDiffuse);
            
            rd = refract(enterDir, enterData.n*enterData.sig, ior);
            data = NEW_DATA;
            if (length(rd) <= 0.0001) {
               // rd = reflect(enterDir, enterData.n*enterData.sig);
               // data.skip = -1;
               // data.sig = -1.;
                //withSky = false;
                break;
            } else {
                data.sig = 1.;
                withSky = i > 0;
               // data.skip = enterData.id;
            }
            ro = enterP+(enterData.n*NEAR*64.*data.sig);
        } else {
            break;
        }
        col = max(col, 0.0);
    }
    data.d = dist;
    return true;
}

#define ID_GROUND 1
#define ID_BUSH 2
#define ID_FOUNTAIN 3
#define ID_WATER 4
#define ID_BENCH 5

float groundSDF(in vec3 p) { return p.y; }
float bushSDF(in vec3 p) {
    vec3 v = p;
    vec3 n1 = vec3(cos(v.x), sin(v.y), sin(v.z));
    vec3 n2 = vec3(sin(v.x), cos(v.y), cos(v.x));
    vec3 n3 = cross(n1, n2);
    vec3 n4 = vec3(cos(n3.x), sin(n3.y), sin(n3.z));
    vec3 n5 = vec3(sin(v.x+n4.x), cos(v.y+n4.y), cos(v.z+n4.z));
    vec3 n6 = reflect(n5, n3);
    
    vec3 n = (n1+n2+n3+n4+n5+n6)/6.;
    p.xz += n.yx*0.25;
    p.y += abs(n.z)*0.25;
    
    float r = 1.0;
    
    float len = 16.0;
    p.z += len/2.;
    float dx = lineSDF(vec3(p.x, p.y, abs(p.z-len/2.)) - vec3(-len/2., r, len/2.), vec3(0., 0, 0), vec3(len, 0, 0), r);
    float dz = lineSDF(vec3(abs(p.x), p.y, p.z) - vec3(len/2., r, 0), vec3(0., 0, 0.), vec3(0., 0, len), r);
    float k = 0.5;
    float d = smin(dx, dz, k);
    
    return d/1.1;
}

#define SDF_SIG inout int id, in int skip, inout Object o

float fountainSDF(in vec3 p, SDF_SIG) {
    id = ID_FOUNTAIN;
    float dist = FAR;
    
    float r = 4.0;
    float h = 0.7;
    float base = max(length(p.xz)-r, p.y-h)-0.05;
    float baseInside = max(max(length(p.xz)-(r*0.85), (p.y)-h*2.)-0.03, -p.y+0.15)-0.02;
    base = smax(base, -baseInside, 0.3)-0.01;
    
    float pedHeight = h*0.9;
    float pedR = r*0.4;
    float ped = cylSDF(p, vec3(0, 0, 0), vec3(0, pedHeight, 0), pedR)-0.04;
    
    base = smin(base, ped, 0.25);
    
    dist = min(dist, base);
    
    float poleHeight = 4.5;
    float poleR = pedR*0.33;
    float pole = cylSDF(p - vec3(0, pedHeight, 0), vec3(0, 0, 0), vec3(0, poleHeight, 0), poleR);
    
    float box = boxSDF(p - vec3(0, (poleHeight/2.)+pedHeight, 0), vec3(poleR, poleHeight/2., poleR));
    float box2 = boxSDF(pointRot(p - vec3(0, (poleHeight/2.)+pedHeight, 0), vec3(0, radians(45.), 0)), vec3(poleR, poleHeight/2., poleR));
    box = min(box, box2);
    pole = mix(pole, box, 0.8*smoothstep(0.0, 1.0, length(p.xz)));
    dist = min(dist, pole);
    
    if (p.y < 3.3 && skip != ID_WATER)
    {
       // vec3 n1 = snoise(p.xz, 0.03921, 0.1, 0.1, 6);
        
        float water = max(max(length(p.xz)-(r*0.85), (p.y)-h/1.5)-0.03, -p.y+0.15)-0.02;
        float waterInside = (max(length(p.xz)-(r*0.85*0.98), (p.y-0.01)-h/2.2)-0.03)-0.02;
        
        if (p.y > 0.02 && water < 2.) {
            vec2 shift = vec2(cos(T*0.25), sin(T*0.25))*0.8;
            vec2 uv = p.xz+(sin(shift));
            uv *= rot(sin(((T*0.5)+0.2*(shift.x-shift.y))*0.1));
           // uv *= mat2(6.28, 8.0, -6.28, 8.0)*0.1;
            vec3 n1 = sineNoise((uv+shift)*0.55, 0.0329212, 0.9);
            uv *= rot(cos(((T*0.5)+n1.z*2.)*0.03)+3.*(0.2+(0.5*(n1.x-n1.y+n1.z))));
            n1 = n1.xzy;
            uv += shift-vec2(cos(shift.x+n1.x), sin(shift.y+n1.y))*n1.z;
          //  uv *= mat2(6.28, 8.0, -6.28, 8.0)*0.1;
            vec3 n2 = exp(vec3(sin(uv.y), cos(uv.x), sin(n1.y+n1.z+n1.x+(uv.x*3.32921+uv.y)))-1.);
            n2 = n2.zxy;
            vec3 n3 = exp(vec3(cos(n1.z-uv.y), sin(n2.y-uv.x), sin(n2.z*uv.y))-1.);
            
            
            float wh = (n1.x+n1.y+n1.z)/3.;
            wh += (n2.x+n2.y+n2.z)/3.;
            wh += 0.45-(n3.x+n3.y+n3.z)/3.;
            wh *= 0.49;
            wh -= 0.1;
            float ra = r;
            float fe = ra*0.5;
            float ra2 = ra*0.5;
            float fe2 = ra2*0.5;
            wh *= (1.0-smoothstep(ra-fe, ra+fe, length(p.xz)));
            wh *= smoothstep(ra2-fe2, ra2+fe2, length(p.xz));
            wh = sclamp(wh, -0.23, 1.0, 0.7);
            wh *= 0.96;
            float hh = sclamp(p.y-0.4, 0.0, 1.0, 1.7)*wh;
            water -= hh;
            waterInside -= hh;
        }
        water = smax(water, -waterInside, 0.05);
        SAMPLE(water, o, ID_WATER);
    }
    return dist;
}

float benchSDF(in vec3 p) {
    float dist = FAR;
    float h = 0.7;
    vec3 seatSize = vec3(1, 0.05, 0.3);
    float seat = boxSDF(p - vec3(0, seatSize.y + h, 0), seatSize)-0.01;
    dist = min(dist, seat);
    
    float legLen = (h-seatSize.y)+0.1;
    float legR = 0.09;
    float leg = cylSDF(vec3(abs(p.x), p.y, abs(p.z)) - vec3((seatSize.x/2.)+legR*2., 0, seatSize.z/2.), vec3(0, 0, 0), vec3(0, legLen, 0), legR) -0.01;
    
    vec3 backSize = vec3(seatSize.x, 0.35, seatSize.y);
    float back = boxSDF(p - vec3(0, backSize.y+seatSize.y+h, seatSize.z), backSize)-0.01;
    dist = min(dist, back);
    
    dist = min(dist, leg);
    
    return dist;
    
}

float sdf(inout Data data, in vec3 p) {
    int skip = data.skip;
    int id = ID_NONE;
    float dist = FAR;
    Object o = NEW_OBJECT;
    
    float ground = groundSDF(p);
    SAMPLE(ground, o, ID_GROUND);
    
    float bush = bushSDF(p);
    SAMPLE(bush, o, ID_BUSH);
    
    float bench = benchSDF(vec3(p.x, p.y, abs(p.z)) - vec3(0, 0., 6.3));
    SAMPLE(bench, o, ID_BENCH);
    
    int fountainId = 0;
    Object oFountain = NEW_OBJECT;
    float fountain = fountainSDF(p, fountainId, skip, oFountain);
    SAMPLE(fountain, oFountain, fountainId);
    
    data.id = id;
    data.o = o;
    return dist;
}

vec3 leaf(in vec2 uv, in vec2 id, in float ra, inout Material m) {
    float ra0 = fract(((ra-id.x)*44.4929811)+id.y*3.3921);
    if (floor(ra0+ra+0.1) <= 0.) { return vec3(0.0); }
    float v = 0.0;
    float len = 0.9;
    float s = smoothstep(len/2., 0.0, abs(uv.y))*smoothstep(len/3., 0.1, -uv.y+0.01);
    vec2 start = vec2(0,-len/2.);
    vec2 end = vec2(0, len/2.);
    start.x += 0.1*s;
    end.x += 0.1*s;
    v += line2D(abs(uv), start, end, 0.01);
    v += smoothstep(0.0, 0.01, dot(vec2(abs(uv.x)-(0.1*s), uv.y), (start-end).yx));
    m.z += v;
    vec3 col = mix(vec3(0.3, 0.33, 0.1), vec3(0.9, 0.9, 0.5), s*s*s);
    vec3 c1 = vec3(0.3, 0.33, 0.2);
    vec3 c2 = vec3(0.8, 0.8, 0.3);
    vec3 c3 = vec3(0.2, 0.5, 0.04);
    vec3 c4 = vec3(0.0, 0.4, 0.02);
    vec3 n = hash23(id, ra);
    vec3 a = mix(c1, c2, n.x);
    vec3 b = mix(c3, c4, n.y);
    vec3 c = mix(a, b, n.z);
    col = mix(col, c, ra*ra);
    float ra2 = fract((ra*10.98938281)+(id.x*3.392912+id.y));
    col = mix(col, (col*c3)+(c2*c3*s), ra2*ra2*0.39);
    m.z += 0.5-s;
    return col*v;
}

vec3 leafsTexture(in vec2 uv, in float iseed, inout Material m) {
    vec3 col = vec3(0.0);
    float tile = 4.0;
    float seed = 3.329291 + iseed;
    float div = 1.0;
    for (int i = ZERO; i < 24; i++) {
        vec2 id = floor(uv*tile);
        vec2 lv = fract(uv*tile);
        vec2 slv = lv*lv*(3.0-2.0*lv);
        float n = hash21(id, seed);
        float n2 = hash21(id, seed+id.x+id.y+0.329812);
        lv = fract(uv*tile);
        lv = (0.5-lv)*rot(n*6.28);
        col = max(col, leaf(lv, id, fract((n+n2+id.x+id.y)*10.4992), m)/div);
        seed += 13.937272;
        tile += 1.;
        uv *= mat2(6.28, 8.0, -6.28, 8.)*0.1;
        div += 0.2;
    }
    m.z /= 24.;
    col = col / (1.0 + max(col-0.56, 0.0));
    
   // m.ior = mix(0.0, 1.500, smoothstep(0.02, 0.0, length(col)));
    
    return col;
}
//#define DEBUG_TEXTURE woodTexture
vec3 woodTexture(in vec2 uv, in vec3 p, inout Material m) {
    uv += 0.5928312;
    float tile = 2.0;
    vec2 id = floor(uv*tile);
    vec2 lv = fract(uv*tile);
    vec2 alv = abs(lv*2.0-1.0);
    lv = lv*lv*(3.0-2.0*lv);
    float seed = 0.392812;
    float rid = mix(
        mix(hash21Fast(id, seed), hash21Fast(id+vec2(1, 0), seed), lv.x),
        mix(hash21Fast(id+vec2(0, 1), seed), hash21Fast(id+vec2(1, 1), seed), lv.x),
        lv.y
    );
    uv *= rot(rid*0.5);
    vec3 col = vec3(0.0);
    vec3 c1 = rgb(142, 107, 94);
    vec3 c2 = rgb(79, 53, 57);
    vec3 c3 = rgb(174, 109, 69);
    vec3 c4 = rgb(232, 186, 128);
    vec3 hf1 = sineNoise2(uv, 0.3292123, 2.);
    uv *= rot(hf1.z*0.5);
    vec3 col1 = mix4(c1, c2, c3, c4, hf1);
    vec3 col2 = mix4(c4, c3, c2, c1, hf1);
    col += (col1+col2)*0.5;
    float cracks = max(0.0, 1.0-smoothstep(0.02, 0.1, abs(hf1.y-rid)));
    float cracks2 = exp((sin((uv.x*4.928931+uv.y)*4.28*2.+cos((uv.y*4.203921)-(uv.x*0.59291))))-1.);
    cracks = mix(cracks, cracks2, clamp(hf1.z+hf1.y, 0.0, 1.0));
    cracks = mix(cracks, fract(cracks*3.3), 0.7*smoothstep(0.4, 0.7, hf1.x));
    cracks *= 0.5;
    col = mix(col, col*col, cracks);
    m.rough = clamp(cracks*2., 0.7, 1.0);
    m.spec = max(0.0, 0.3-cracks*2.);
    m.spec += hf1.y*0.5;
    m.z = 0.3-cracks*0.6;
    return col;
}

//#define DEBUG_TEXTURE stoneTexture

vec3 stoneTexture(in vec2 uv, in vec3 p, inout Material m) {
    vec3 col = vec3(0.0);
    vec3 c1 = rgb(84, 90, 90);
    vec3 c2 = rgb(198, 194, 178);
    vec3 c3 = rgb(100, 104, 90);
    vec3 c4 = rgb(111, 75, 35);
    
    vec3 warp = snoiseWarp(uv, 0.092831, 0.9, 16.0, 6, 0.5);
    vec3 lf1 = snoise(uv, 4.494983, 0.2, 8.0, 6);
    vec3 hf1 = snoise(uv, 9.998255, 0.3, 32.0, 6);
    vec3 col1 = mix4(c1, c2, c3, c4, warp);
    vec3 col2 = mix4(c4, c3, c2, c1, warp);
    vec3 col3 = mix4(c1, c2, c3, c4, hf1);
    vec3 col4 = mix4(c4, c3, c2, c1, hf1);
    vec3 crackCol = (col3+col4)/2.;
    crackCol = clamp(crackCol*(0.25+crackCol), 0.0, 1.0);
    float dirtReg = smoothstep(0.4, 0.7, lf1.x);
    col = mix(col2, col1, dirtReg);
    
    float cracks = 1.0-smoothstep(0.0, 0.05, abs(hf1.x-lf1.z));
    cracks *= smoothstep(0.4, 0.7, warp.z);
    col = mix(col, crackCol, cracks);
    
    m.spec = clamp(1.0-0.2*((dirtReg+cracks)*(0.5*(dirtReg+cracks))),0.25, 1.0);
    m.rough = clamp((dirtReg+cracks)/2., 0.6, 1.0);
    
    m.z = 0.5 - 0.5*(dirtReg+cracks);
    
    return col;
}
//#define DEBUG_TEXTURE stoneBrickTexture

vec3 stoneBrickTexture(in vec2 uv, in vec3 p, inout Material m) {
    vec3 col = vec3(0.0);
    vec3 c1 = rgb(143, 134, 121);
    vec3 c2 = rgb(130, 128, 124);
    vec3 c3 = rgb(71, 67, 62);
    vec3 c4 = rgb(164, 145, 122);
    
    vec3 g1 = rgb(58, 76, 39);
    vec3 g2 = rgb(77, 75, 54);
    vec3 g3 = rgb(107, 102, 72);
    vec3 g4 = rgb(85, 107, 44);
    
    vec3 lf1 = snoise(uv, 0.0392219, 0.2, 16.0, 6);
    vec3 lf2 = snoise(uv, 7.7777793, 0.2, 8.0, 6);
    vec3 hf1 = snoise(uv, 5.5598393, 0.5, 64.0, 6);
    vec3 hf2 = snoise(uv, 16.956838, 0.0, 128., 6);
    vec3 grain = abs(normalize(cross(hf1*2.0-1.0, hf2*2.0-1.0)));
    
    float cracks = 1.0-smoothstep(0.04, 0.1, abs((lf2.y+(lf1.y*2.0-1.0))*2.0-1.0));
    cracks *= smoothstep(0.4, 0.7, lf2.z);
    
    
    vec3 grassCol1 = mix4(g1, g2, g3, g4, smoothstep(0.19, 0.3, hf2*lf1));
    vec3 grassCol2 = mix4(g4, g3, g2, g1, hf2);
    vec3 grassCol = mix(grassCol1, grassCol2, lf2.x);
    float grassGrain = grain.x*0.4;
    grassCol = mix(grassCol, grassCol*grassCol, grassGrain);
    
    vec3 col1 = mix4(c1, c2, c3, c4, hf1);
    vec3 col2 = mix4(c4, c3, c2, c1, hf1);
    vec3 colA = (col1+col2)*0.5;
    
    vec3 col3 = mix4(c1, c2, c3, c4, smoothstep(0.4, 0.7, lf1));
    vec3 col4 = mix4(c4, c3, c2, c1, smoothstep(0.4, 0.7, lf1));
    vec3 colB = (col3+col4)*0.5;
    
    col = mix(colA, colB, smoothstep(0.4, 0.7, lf2.x));
    
    
    float tile = 4.0;
    float idy = floor(uv.y*tile);
    vec2 uuv = uv;
    uuv.x += idy/tile/2.;
    vec2 id = floor(uuv*tile);
    vec2 lv = fract(uuv*tile);
    vec2 alv = abs(lv*2.0-1.0);
    
    float idr = hash21Fast(id*4.492921, 3.32092912+id.x+id.y);
    float idr2 = hash21Fast(id*6.694912, 7.7779894+id.x-id.y+idr);
    float idr3 = hash21Fast(id*10.293123, 0.0923811+idr+idr2);
    
    vec3 idf = vec3(idr, idr2, idr3);
    vec3 idcol1 = mix4(c1, c2, c3, c4, idf);
    vec3 idcol2 = mix4(c4, c3, c2, c1, idf);
    vec3 idcol = (idcol1+idcol2)*0.5;
    
    float stoneR = mix(1.4, 0.5, smoothstep(0.4, 0.7, lf1.x));
    float stoneRX = mix(-0.01, 0.2, idr);
    float stoneRY = mix(-0.01, 0.2, idr2);
    stoneR = max(0.02, stoneR-0.5);
    float stoneArea = clamp((1.0-max(
        smoothstep(0.48-stoneR, 0.55, alv.x-(0.5-stoneRX)),
        smoothstep(0.48-stoneR, 0.55, alv.y-(0.5-stoneRY))))*3., 0.0, 1.0);

    float grassArea = max(0.0, 1.0-stoneArea);
    cracks *= stoneArea;
    
    float stoneGrain = grain.y*0.4*stoneArea*max(0.25, smoothstep(0.4, 0.7, lf2.z));
    
    col = mix(col, idcol, stoneArea);
    
    float edge = 1.0-smoothstep(-0.1, 0.9, abs(stoneArea - grassArea));
    
    grassArea *= smoothstep(0.2, 0.7, lf1.z);
    col = mix(col, grassCol, grassArea);
    
    col = mix(col, col*col, edge);
    float corner = (edge+stoneR*0.05)*max(smoothstep(0.33, 0.54, lv.x-(0.3-stoneRX)),
                            smoothstep(0.33, 0.54, lv.y-(0.3-stoneRY)));
    corner = corner*corner;
    col = mix(col, clamp(col+col+col, 0.0, 1.0), corner*0.8);
    col = mix(col, colA*colB, cracks);
    col = mix(col, col*col*col, stoneGrain);
    
    m.spec = max(0.0, 1.0-(grassArea+cracks));
    m.z = (stoneArea-grassArea)-(cracks*0.15);
    m.rough = clamp(((grassArea+cracks)*2.)+(stoneGrain*2.), 0.56, 1.0);
    return col;
}

vec3 getAlbedoGround(inout Data data) {
    vec3 p = data.p;
    vec3 n = data.n;
    vec2 uv = boxUv(p, n);
    uv *= 0.5;
    vec3 col = stoneBrickTexture(uv, p, data.m);
    data.n = makeNormal(data.n, data.m.z, 0.23, 0.009);
    return col;
}

vec3 getAlbedoBench(inout Data data) {
    vec3 p = data.p;
    vec3 n = data.n;
    vec2 uv = boxUv(p, n);
    vec3 col = woodTexture(uv, p, data.m);
    data.n = makeNormal(data.n, data.m.z, 0.17, 0.001);
    return col;
}

vec3 getAlbedoBush(inout Data data) {
    vec3 p = data.p;
    vec3 n = data.n;
    vec2 uv = boxUv(p, n);
    uv *= 0.5;
    vec3 col = leafsTexture(uv, 0.32123, data.m);
    data.n = makeNormal(data.n, data.m.z, 0.6, 0.1);
    return col;
}

vec3 getAlbedoWater(inout Data data) {
    vec3 p = data.p;
    vec3 n = data.n;
    vec2 uv = boxUv(p, n);
    vec3 col = vec3(0.15, 0.43, 0.59);
    col = ((col*col)+(col*col))*0.25;
    data.m.ior = 1.33;
    data.m.rough = 0.1;
    data.m.metallic = 0.8934;
    return col;
}

vec3 getAlbedoFountain(inout Data data) {
    vec3 p = data.p;
    vec3 n = data.n;
    vec2 uv = boxUv(p, n);
    vec3 col = stoneTexture(uv, p, data.m);
    data.n = makeNormal(data.n, data.m.z, 0.23, 0.009);
    return col;
}

vec3 getAlbedo(inout Data data) {
    switch (data.id) {
        case ID_GROUND: return getAlbedoGround(data); break;
        case ID_BENCH: return getAlbedoBench(data); break;
        case ID_BUSH: return getAlbedoBush(data); break;
        case ID_WATER: return getAlbedoWater(data); break;
        case ID_FOUNTAIN: return getAlbedoFountain(data); break;
    }
    
    return vec3(0.77);
}

vec3 getSky(in vec3 rd) {
    float dotup = cdot(rd, vec3(0, 1, 0));
    vec3 col = pow(COLOR_SKY, vec3(1.0 + dotup));
    return col;
}

vec3 render(inout Data data, in vec3 ro, in vec3 rd, inout float depth) {
    vec3 col = vec3(0.0);
    depth = 1.0;
    float dotup = cdot(rd, vec3(0, 1, 0));
    
    Light lights[NUM_LIGHTS];
    lights[0] = LIGHT_SUN; 
    
    raycast(data, lights, col, ro, rd);
    
    depth = data.d / FAR;
    
    col += smoothstep(0.01, 1.0, depth) * max(0.0, 1.0-smoothstep(0.0, 0.25, dotup));
    
    return col;
}

Ray getRay(in vec2 uv, in vec4 m) {
    vec3 ro = vec3(0, 0, -10.0);
    vec3 rd = normalize(vec3(uv.xy, 1.0));
    
    if (m.z > 0.001 && length(m.xy) > 0.0001 && iFrame > 1 && iTime > 0.0001) {
        ro.yz *= rot(m.y*TAU);
        ro.xz *= rot(m.x*TAU);
        
        rd.yz *= rot(m.y*TAU);
        rd.xz *= rot(m.x*TAU);
    } else {
        ro.y += 2.5;
        ro.xz = vec2(cos(T*0.33), sin(T*0.33))*10.;
        rd = look(uv, vec3(0, 0, 0), ro, 1.);
    }
    
    float y = 1.5;
    ro.y += y;
    ro.y = max(max(ro.y, y), 3.3);
    
    return Ray(ro, rd);
}

void mainImage( out vec4 O, in vec2 fc )
{
    vec3 col = vec3(0.0);
    vec2 uv = (fc-0.5*R.xy)/R.y;
    vec4 m = vec4((iMouse.xy-0.5*R.xy)/R.y, iMouse.zw);
    
    Ray ray = getRay(uv, m);
    vec3 ro = ray.ro;
    vec3 rd = ray.rd;
    Data data = NEW_DATA;
    float depth = 1.0;
    
    #ifdef DEBUG_TEXTURE
    Material ma = NEW_MATERIAL;
    col += DEBUG_TEXTURE(fc.xy/R.xy, ro+rd, ma);
    #else
    col += render(data, ro, rd, depth);
    col = max(col, 0.0);
    col = aces(col);
    col = pow(col, vec3(1.0 / 2.2));
    #endif
    O = vec4(clamp(col, 0.0, 1.0), 1.0);
}
