#define MOD3 vec3(.1031,.11369,.13787)
#define NUM_CELLS	8.0	// Needs to be a multiple of TILES!
#define TILES 		1.0		// Normally set to 1.0 for a creating a tileable texture.

layout(location = 0) out vec4 noiseOUT;

uniform int 		layer;
const float PI=3.1415926535;

//-----------------------------------------------------------------------------
/*float hash(float n)
{
	return fract( sin(n) * 43758.5453 );
}

vec2 hash( vec2 p ) 
{  						// rand in [-1,1]
	p = vec2( dot(p,vec2(127.1,311.7)),
			  dot(p,vec2(269.5,183.3)) );
	return -1. + 2.*fract(sin(p+20.)*53758.5453123);
}*/

vec3 hash33(vec3 p3)
{
	p3 = fract(p3 * MOD3);
    p3 += dot(p3, p3.yxz+19.19);
    return -1.0 + 2.0 * fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
}

//-----------------------------------------------------------------------------
// hash based 3d value noise
/*float noise(in vec3 x)
{
	vec3 p = floor(x);
	vec3 f = fract(x);

	f = f*f*(3.0 - 2.0 * f);
	float n = p.x + p.y*57.0 + 113.0*p.z;
	return mix(
			mix(
				mix(hash(n + 0.0), hash(n + 1.0), f.x),
				mix(hash(n + 57.0), hash(n + 58.0), f.x),
				f.y),
			mix(
				mix(hash(n + 113.0), hash(n + 114.0), f.x),
				mix(hash(n + 170.0), hash(n + 171.0), f.x),
				f.y),
			f.z);
}*/

//////////////////////////////////////////////////////////////////////////
#define NOISE fbm
#define NUM_NOISE_OCTAVES 5


float hash(float n) { return fract(sin(n) * 1e4); }
float hash(vec2 p) { return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x)))); }

float noise(float x) {
    float i = floor(x);
    float f = fract(x);
    float u = f * f * (3.0 - 2.0 * f);
    return mix(hash(i), hash(i + 1.0), u);
}

float noise(vec2 x) {
    vec2 i = floor(x);
    vec2 f = fract(x);

	// Four corners in 2D of a tile
	float a = hash(i);
    float b = hash(i + vec2(1.0, 0.0));
    float c = hash(i + vec2(0.0, 1.0));
    float d = hash(i + vec2(1.0, 1.0));

    // Simple 2D lerp using smoothstep envelope between the values.
	// return vec3(mix(mix(a, b, smoothstep(0.0, 1.0, f.x)),
	//			mix(c, d, smoothstep(0.0, 1.0, f.x)),
	//			smoothstep(0.0, 1.0, f.y)));

	// Same code, with the clamps in smoothstep and common subexpressions
	// optimized away.
    vec2 u = f * f * (3.0 - 2.0 * f);
	return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

float noise(vec3 x) 
{
    const vec3 step = vec3(110, 241, 171);

    vec3 i = floor(x);
    vec3 f = fract(x);
	i=mod(i,8.0);
 
    // For performance, compute the base input to a 1D hash from the integer part of the argument and the 
    // incremental change to the 1D based on the 3D -> 1D wrapping
    float n = dot(i, step);

    vec3 u = f * f * (3.0 - 2.0 * f);
    return mix(mix(mix( hash(n + dot(step, vec3(0, 0, 0))), hash(n + dot(step, vec3(1, 0, 0))), u.x),
                   mix( hash(n + dot(step, vec3(0, 1, 0))), hash(n + dot(step, vec3(1, 1, 0))), u.x), u.y),
               mix(mix( hash(n + dot(step, vec3(0, 0, 1))), hash(n + dot(step, vec3(1, 0, 1))), u.x),
                   mix( hash(n + dot(step, vec3(0, 1, 1))), hash(n + dot(step, vec3(1, 1, 1))), u.x), u.y), u.z);
}


float fbm(float x) {
	float v = 0.0;
	float a = 0.5;
	float shift = float(100);
	for (int i = 0; i < NUM_NOISE_OCTAVES; ++i) {
		v += a * noise(x);
		x = x * 2.0 + shift;
		a *= 0.5;
	}
	return v;
}


float fbm(vec2 x) {
	float v = 0.0;
	float a = 0.5;
	vec2 shift = vec2(100);
	// Rotate to reduce axial bias
    mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50));
	for (int i = 0; i < NUM_NOISE_OCTAVES; ++i) {
		v += a * noise(x);
		x = rot * x * 2.0 + shift;
		a *= 0.5;
	}
	return v;
}


float fbm(vec3 x) {
	float v = 0.0;
	float a = 0.5;
	vec3 shift = vec3(100);
	for (int i = 0; i < NUM_NOISE_OCTAVES; ++i) {
		v += a * noise(x);
		x = x * 2.0 + shift;
		a *= 0.5;
	}
	return v;
}

//------------------------------------------------------------------------
float worley3D(in vec3 p, in float numCells)
{
	p *= numCells;
	float d = 1.0e10;
	for (int xo = -1; xo <= 1; xo++)
	{
		for (int yo = -1; yo <= 1; yo++)
		{
            for (int zo = -1; zo <= 1; zo++)
            {
                vec3 tp = floor(p) + vec3(xo, yo, zo);
                tp = p - tp - noise(mod(tp, numCells / TILES));
                d = min(d, dot(tp, tp));
            }
		}
	}
    float f = 0.0;//mix(0.0, 0.0, noise(p * 5.0));
	return smoothstep(-2.0, 2.0, 1.0 - d  * 3.7 + f);
}


float perlin(vec3 p)
{
    vec3 pi = floor(p);
    vec3 pf = p - pi;
    
    vec3 w = pf * pf * (3.0 - 2.0 * pf);
    
    return 	mix(
        		mix(
                	mix(dot(pf - vec3(0, 0, 0), hash33(pi + vec3(0, 0, 0))), 
                        dot(pf - vec3(1, 0, 0), hash33(pi + vec3(1, 0, 0))),
                       	w.x),
                	mix(dot(pf - vec3(0, 0, 1), hash33(pi + vec3(0, 0, 1))), 
                        dot(pf - vec3(1, 0, 1), hash33(pi + vec3(1, 0, 1))),
                       	w.x),
                	w.z),
        		mix(
                    mix(dot(pf - vec3(0, 1, 0), hash33(pi + vec3(0, 1, 0))), 
                        dot(pf - vec3(1, 1, 0), hash33(pi + vec3(1, 1, 0))),
                       	w.x),
                   	mix(dot(pf - vec3(0, 1, 1), hash33(pi + vec3(0, 1, 1))), 
                        dot(pf - vec3(1, 1, 1), hash33(pi + vec3(1, 1, 1))),
                       	w.x),
                	w.z),
    			w.y);
}

float tileableNoise(vec3 uv)
{
	/*float xt=mix(mod(uv.x,8.0),uv.x,0.5);
	float yt=mix(mod(uv.y,8.0),uv.y,0.5);
	float zt=mix(mod(uv.z,8.0),uv.z,0.5);
*/
//	float val = perlin( xt,yt,zt );
	float f=noise(uv+vec3(1/8.0,0.0,0.0));
	f+=noise(uv+vec3(0.0,1/8.0,0.0));
	f+=noise(uv+vec3(0.0,0.0,1/8.0));
	f+=noise(uv+vec3(-1/8.0,0.0,0.0));
	f+=noise(uv+vec3(0.0,-1/8.0,0.0));
	f+=noise(uv+vec3(0.0,0.0,-1/8.0));
	return f/6.0;
}

float tileableNoise2(vec3 uv)
{
	/*float xt=mix(mod(uv.x,8.0),uv.x,0.5);
	float yt=mix(mod(uv.y,8.0),uv.y,0.5);
	float zt=mix(mod(uv.z,8.0),uv.z,0.5);
*/
//	float val = perlin( xt,yt,zt );
	float f=tileableNoise(uv+vec3(2/8.0,0.0,0.0));
	f+=tileableNoise(uv+vec3(0.0,2/8.0,0.0));
	f+=tileableNoise(uv+vec3(0.0,0.0,2/8.0));
	f+=tileableNoise(uv+vec3(-2/8.0,0.0,0.0));
	f+=tileableNoise(uv+vec3(0.0,-2/8.0,0.0));
	f+=tileableNoise(uv+vec3(0.0,0.0,-2/8.0));
	return f/6.0;
}

float noise_sum(vec3 p)
{
    float f = 0.0;
    f += 1.0000 * tileableNoise2(p); p = 2.0 * p;
    f += 0.5000 * tileableNoise2(p); p = 2.0 * p;
	f += 0.2500 * tileableNoise2(p); p = 2.0 * p;
	f += 0.1250 * tileableNoise2(p); p = 2.0 * p;
	f += 0.0625 * tileableNoise2(p); p = 2.0 * p;
    
    return f*0.5;
}

float worley_sum(vec3 p, float nCells)
{
    float f = 0.0;
    f += 0.5000 * worley3D(p,nCells);
    f += 0.25000 * worley3D(p,nCells*2.0);
	f += 0.12500 * worley3D(p,nCells*4.0);
	f += 0.0625 * worley3D(p,nCells*8.0);
	f += 0.03125 * worley3D(p,nCells*16.0);
    
	//f=(f-0.1)*1.1;
	
    return f;
}
	
	
void main()
{
#ifdef CLOUD_SHAPE
	vec3 uv = vec3(gl_FragCoord.x/S2_SHAPE_CLOUD_WIDTH,gl_FragCoord.y/S2_SHAPE_CLOUD_HEIGHT,float(layer)/float(S2_SHAPE_CLOUD_DEPTH));
	float f1=worley_sum(uv,NUM_CELLS*0.5);//*0.0625);
	float f2=worley_sum(uv,NUM_CELLS);//0.125);
	float f3=worley_sum(uv,NUM_CELLS*2.0);//0.25);
	float f4=((noise_sum(uv*8.0)+(worley3D(uv,NUM_CELLS)*0.125))-0.2)*1.8;

	noiseOUT.xyz=vec3(f1,f2,f3);
	noiseOUT.w=f4;
#endif

#ifdef CLOUD_DETAIL
	vec3 uv = vec3(gl_FragCoord.x/S2_DETAIL_CLOUD_WIDTH,gl_FragCoord.y/S2_DETAIL_CLOUD_HEIGHT,float(layer)/float(S2_DETAIL_CLOUD_DEPTH));
	float f1=worley3D(uv,NUM_CELLS*0.5);
	float f2=worley3D(uv,NUM_CELLS*2.0);
	float f3=worley3D(uv,NUM_CELLS*4.0);
	
	noiseOUT.xyz=vec3(f1,f2,f3);
	noiseOUT.w=1.0;
#endif

	/*vec2 uv = gl_FragCoord.xy;
	vec3 pos = vec3(uv*0.0025 * vec2(screenSize.x/screenSize.y, 1.0), iTime*0.1);
    float f1= worley_sum(uv.xy*0.5, 32.0);
    float f2= noise_sum(pos);
	output = vec4(vec3(f1*f2),1.0)*/
	
}

