/*
   fakesubss2.sl Fake Sub-Surface Scattering Class-Based Shader
   Written by: Tim Chrismer
   Special Thanks to Stephen Winters for his Voronoi Noise methods and Jeff Wyner for 
   his examples of the implementations
*/
  
#define vsnoise(x) (2*(vector noise(x))-1)
  
  
void 
voronoi_f1f2_3d (point P;
        float jitter;
        output float f1; output point pos1;
        output float f2; output point pos2;)
{
    point thiscell = point (floor(xcomp(P))+0.5,floor(ycomp(P))+0.5,floor(zcomp(P))+0.5);
    f1 = f2 = 1000;
    uniform float i,j,k;
    for (i = -1; i<=1; i+=1){
        for (j=-1; j<=1; j+=1){
            for (k=-1; k<=1; k+=1){
                point testcell = thiscell + vector(i,j,k);
                point pos = testcell 
                    + jitter * (vector cellnoise (testcell) -0.5);
                vector offset = pos - P;
                float dist = offset . offset;
                if (dist < f1) {
                    f2 = f1; pos2 = pos1;
                    f1 = dist; pos1 = pos;
                } else if (dist < f2){
                    f2 = dist; pos2 = pos;
                }
            }
        }
    }
    f1 = sqrt (f1); f2 = sqrt(f2);
}
  
vector 
vfBm(point p; uniform float octaves, lacunarity, gain)
{
    uniform float amp = 1;
    varying point pp = p;
    varying vector sum = 0;
  
    uniform float i;
    
    for (i=0; i<octaves; i+=1){
        sum += amp * vsnoise(pp);
        amp *= gain; pp*=lacunarity;
    }
    return sum;
}
  
  
  
color envmapping (normal norm;
                  vector in;
                  string mapname;
                  string coordname;
                  float kenv)
{
    color envcolor = 0;
    if(mapname != ""){
        normal n = normalize(norm);
        vector i = normalize(in);
        normal nf = faceforward(n, i);
        
        vector R = reflect(i, nf);
        R = transform(coordname, R);
        envcolor = environment(mapname, R) * kenv;
    }
    return envcolor;
}
  
color layer (normal norm;
             vector in;
             float ka, kd, ks,roughness;
             color hilitecolor)
{
    normal n = normalize(norm);
    vector i = normalize(in);
    normal nf = faceforward(n, i);
    color amb = ka * ambient();
    color dif = kd * diffuse(nf);
    color spc = ks * specular(nf, -i, roughness) * hilitecolor;
    
    return (amb + dif + spc);
}
  
  
class fakesubss3(float Kd = 1,       /* basic brightness */
                       Ks = 0.7,     /* hilight brightness */
                       Ka = .5,         /* ambient brightness */
                       subKa = 1,    /* subsurf amb " */
                       subKd = 0.5,  /* subsurf diffuse " */
                       subKs = 0.7,  /* subsurf hilight " */
                       roughness = 0.5,/* roughness of the surface */
                       Km = 0.1,               /* displacement magnitude */
                       CellJitter = 0.5,       /* jitter for each cell */
                         CellFreq = 4.0,         /* frequency of cells */
                        RutFreq = 0.15,            /* frequency of border edges */
                         RutJitter = 1.0,     /* jitter for the border edges */
                        RutThickness = 0.1,  /* thickness of the edges */
                        RutSoftness = 0.1,   /* softness or harshness of the ruts */
                       Kenv = 0.4,     /* environment contrib */
                       Ktop = 0.5,   /* scales the top color */
                       Ksub = 0.5;   /* scales the sub color */
                 color hicolor = 0.8,  /* top hilight color */
                       locolor = 0.5,  /* sub-surf hilight color */
                       subCs = (0.5, 0.55, 0.3);    /* sub-surf color */
                 string envname = "",
                        envspace = "world")
{
    varying normal originalN = 0;
    varying point originalP, displacedP;
    varying normal displacedN;
  
  
  
public void displacement(output point P; output normal N) {
        originalP = P;
        originalN = N;
        normal n = normalize(N);
        
        point pp = originalP;
  
        float Cells, fcolor = 1, bcolor = 0;
  
        point pos1, pos2;
        float f1, f2;
        
        //Jitter in each cell//
        point PP = (pp + RutFreq * vfBm(2*pp, 4, 2, CellJitter));
        
        //function called//
        voronoi_f1f2_3d (PP*CellFreq, RutJitter, f1, pos1, f2, pos2);
        
        //Here I create the cells                
        Cells = smoothstep (RutThickness-RutSoftness, RutThickness+RutSoftness, f2-f1); 
            
        float hump =  (smoothstep (bcolor, fcolor, Cells))*-1; // get some displacement value
        P = P - n * hump * Km;
        N = calculatenormal(P);
        
        displacedP = P;
        displacedN = N;
        P = originalP;
        N = originalN;
    }
  
public void surface(output color Ci, Oi) {
  
    vector i = normalize(I);
    normal n = normalize(N);
  
    color envcolor = envmapping(n, i, envname, envspace, Kenv);
    color topcolor = layer(n, i, Ka, Kd, Ks, roughness, hicolor) * Ktop;
    
    color sublight = layer(normalize(displacedN), i, subKa, subKd, subKs, roughness, locolor) * Ksub;
    color subcolor = sublight * subCs;
    
    Oi = Os;
    Ci = Oi * Cs * (envcolor + topcolor + subcolor);
    }
  
  
}