#ifndef _PERLINNOISE_FXH_
#define _PERLINNOISE_FXH_
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//  Perlin's Official Improved Noise by Ken Perlin
//                                     http://mrl.nyu.edu/~perlin/noise/
//  Impliment by kingeric1992                                 Nov.4.2017
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//rnd rnd generator
int PerlinNoise_rnd( float3 uv) {
    return frac(sin(dot(uv, float3(12.9898, 78.233, 0.025216))) * 43758.5453) * 512; // [0, 65535]
}

float PerlinNoise_grad(int hash, float3 d) {
    int   h = hash & 15;               
    float u = (h<8) ? d.x : d.y;          
    float v = (h<4) ? d.y : ((h == 12 || h == 14)? d.x : d.z);
    return (( h & 1 )? -u : u) + (( h & 2 )? -v : v);
}

float3 PerlinNoise_fade(float3 t) {
    return t*t*t*(t*(t*6.0-15.0)+10.0);
}

//z == frac(Timer.x * 16777216) for dynamic noise
float PerlinNoise( float3 uv ) 
{
    int3   D = floor(uv);
    float3 d = uv - D;
    float3 f = PerlinNoise_fade(d);
    float2 c = {0.0, 1.0};
      
    int h[8] = {
        PerlinNoise_rnd(D+c.xxx), PerlinNoise_rnd(D+c.yxx), 
        PerlinNoise_rnd(D+c.xyx), PerlinNoise_rnd(D+c.yyx),
        PerlinNoise_rnd(D+c.xxy), PerlinNoise_rnd(D+c.yxy),
        PerlinNoise_rnd(D+c.xyy), PerlinNoise_rnd(D+c.yyy)
    };
      
    return lerp(lerp(lerp( PerlinNoise_grad(h[0],d-c.xxx), PerlinNoise_grad(h[1],d-c.yxx), f.x), 
                     lerp( PerlinNoise_grad(h[2],d-c.xyx), PerlinNoise_grad(h[3],d-c.yyx), f.x), f.y),
                lerp(lerp( PerlinNoise_grad(h[4],d-c.xxy), PerlinNoise_grad(h[5],d-c.yxy), f.x), 
                     lerp( PerlinNoise_grad(h[6],d-c.xyy), PerlinNoise_grad(h[7],d-c.yyy), f.x), f.y), f.z);
}

#endif //_PERLINNOISE_FXH_