// Tiling algorithms
// Copyright © 2015 Inigo Quilez

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform vec2      dimension;
uniform vec2      surfaceDimension;
uniform sampler2D surface;
uniform int       type;
uniform float     seed;

vec4 hash4( vec2 p ) { return fract(sin(vec4( 1.0 + seed + dot(p, vec2(37.0, 17.0)), 
                                              2.0 + seed + dot(p, vec2(11.0, 47.0)),
                                              3.0 + seed + dot(p, vec2(41.0, 29.0)),
                                              4.0 + seed + dot(p, vec2(23.0, 31.0)))) * 103.0); }
    
vec4 randomSample( in vec2 uv ) {
    vec2 iuv = floor( uv );
    vec2 fuv = fract( uv );
    
    vec4 ofa = hash4( iuv + vec2(0.0, 0.0) );
    vec4 ofb = hash4( iuv + vec2(1.0, 0.0) );
    vec4 ofc = hash4( iuv + vec2(0.0, 1.0) );
    vec4 ofd = hash4( iuv + vec2(1.0, 1.0) );
    
    // transform per-tile uvs
    ofa.zw = vec2(sign(ofa.zw - 0.5));
    ofb.zw = vec2(sign(ofb.zw - 0.5));
    ofc.zw = vec2(sign(ofc.zw - 0.5));
    ofd.zw = vec2(sign(ofd.zw - 0.5));
    
    // uv's, and derivarives (for correct mipmapping)
    vec2 uva = uv * ofa.zw + ofa.xy;
    vec2 uvb = uv * ofb.zw + ofb.xy;
    vec2 uvc = uv * ofc.zw + ofc.xy;
    vec2 uvd = uv * ofd.zw + ofd.xy;
    
    // fetch and blend
    vec2 b = smoothstep(0.25, 0.75, fuv);
    
    return mix( mix( texture2D( surface, fract(uva) ), 
                     texture2D( surface, fract(uvb) ), b.x ), 
                mix( texture2D( surface, fract(uvc) ),
                     texture2D( surface, fract(uvd) ), b.x), b.y );
}

vec3 cellSample( in vec2 uv, float v ) {
    vec2 p = floor( uv );
    vec2 f = fract( uv );
	
	vec3 va  = vec3(0.0);
	float w1 = 0.0;
    float w2 = 0.0;
    
    for( int j=-1; j<=1; j++ )
    for( int i=-1; i<=1; i++ ) {
        
        vec2 g  = vec2( float(i), float(j) );
		vec4 o  = hash4( p + g );
		vec2 r  = g - f + o.xy;
		float d = dot(r, r);
        float w = exp(-5.0 * d );
        vec3 c  = texture2D( surface, fract(uv + v * o.zw) ).xyz;
        
		va += w * c;
		w1 += w;
        w2 += w * w;
    }
    
    return va / w1;
}

float sum( vec3 v ) { return v.x + v.y + v.z; }

vec3 onionSample( in vec2 x, float v ) {
    float k = hash4( x * 0.005 ).x;
    
    float l = k * 8.0;
    float f = fract(l);
    
    float ia = floor(l); // my method
    float ib = ia + 1.0;
    
    vec2 offa = sin(vec2(3.0, 7.0) * ia); // can replace with any other hash
    vec2 offb = sin(vec2(3.0, 7.0) * ib); // can replace with any other hash
    
    vec3 cola = texture2D( surface, fract(x + v * offa) ).xyz;
    vec3 colb = texture2D( surface, fract(x + v * offb) ).xyz;
    
    return mix( cola, colb, smoothstep(0.2, 0.8, f - 0.1 * sum(cola - colb)) );
}

void main() {
    vec2 surfRat = dimension / surfaceDimension;
    vec2 posRat  = v_vTexcoord * surfRat;
    
         if(type == 0) gl_FragColor = texture2D( surface, fract(posRat) );
    else if(type == 1) gl_FragColor = randomSample( posRat );
    else if(type == 2) gl_FragColor = vec4(cellSample( posRat, 4. ), 1.);
    else if(type == 3) gl_FragColor = vec4(onionSample( posRat, 4. ), 1.);
}