/* Copyright (c) 2001, Chris Lynch/Artdream Designs
 *
 * All rights reserved.
 *
 * Contact:      chris@artdreamdsigns.com
 * Information:  http://www.artdreamdesigns.com
 *
 * This is a C implementation of the BMRT ceramictiles shader written by Larry
 * Gritz. The main difference is that Renderman functions such as specular,
 * ambient, etc. have been removed. It also does not do displacement mapping (take
 * it up with NAN. However, it does do bump mapping.
 * This plugin module calls the module ceramictiles in order to do the shading
 * functions.
 * The controls should be pretty set explanatory. The best way to understand them
 * is to play around with them.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


#include "math.h"
#include "plugin.h"
#include "rm.h"
/*
#include "stdio.h"
#include "RMophdr.h"
*/

float RotN, RotX, RotY, ScaleX;
float ds[3], dt[3];

void ceramictiles (float *result, float ss, float dss, float tt, float dtt,
         float mortarcolor[3],
            float stilespacing, float ttilespacing,
            float groovewidth, float grooveheight, float groovedepth,
            float edgevary, float mottling, float speckly, float mottlefreq,
            float Cbase[3], float Cedge[3], float Cmottle[3], 
                float Cspeck[3], float varyhue, float varysat, float varylum, 
                float varynormal);

float *tiletexture (float tileindex,
                 float stile, float ttile, float ds, float dt,
               float edgevary, float mottling, float speckly,
               float mottlefreq, float Cbase[3], float Cedge[3], 
               float Cmottle[3], float Cspeck[3]);

float *MaterialCeramicTiles (float Nf[3], float Cmortar[3], float Ctile[3],
         float intile);

float *varyEach (float Cin[3], float index, float varyhue,
                        float varysat, float varylum);
float *HsltoRgb(float hsl[3]);
float *RgbtoHsl (float c[3]);
float tilepattern (float ss, float tt, float ds, float dt,
             float groovewidth, float grooveheight,
             float *swhichtile, float *twhichtile,
             float *stile, float *ttile);

/* ******************** GLOBAL VARIABLES ***************** */

char name[]= "RMCeramicTiles";

/* Subtype names must be less than 15 characters */

#define NR_TYPES   1
char stnames[NR_TYPES][16]= {"Default"};

VarStruct varstr[]= {
   { NUM|FLO,   "stilespacing",     1.0,  0.0, 50.0,   
      "hor-tilespacing" },
   { NUM|FLO,   "ttilespacing",      1.0,  0.0, 50.0,   
      "ver-tilespacing" },
   { NUM|FLO,   "groovewidth",      0.06,  0.0, 1.0 ,  
      "groove width" },
   { NUM|FLO,   "grooveheight",      0.06,  0.0, 1.0,   
      "groove height" },
   { NUM|FLO,   "groovedepth",      0.20,  0.0, 1.0,   
      "groove depth" },
   { NUM|FLO,   "edgevary",          1.00,  0.0, 1.0,   
      "edgevary" },
   { NUM|FLO,   "mottling",         1.00,  0.0, 1.0,   
      "mottling" },
   { NUM|FLO,   "speckly",         1.00,  0.0, 1.0,   
      "speckly" },
   { NUM|FLO,   "mottlefreq",      1.00,  0.0, 50.0,  
       "mottlefreq" },
   { NUM|FLO,   "varyhue",         0.25,  0.0, 1.0,   
      "varyhue" },
   { NUM|FLO,   "varysat",         0.40,  0.0, 1.0,   
      "varysat" },
   { NUM|FLO,   "varylum",         0.50,  0.0, 1.0,   
      "varylum" },
   { NUM|FLO,   "varynormal",      0.50,  0.0, 1.0,   
      "varynormal" },
   { NUM|FLO,   "Cbasered",            0.50,  0.0, 1.0,   
      "base color red " },
   { NUM|FLO,   "Cbasegreen",      0.50,  0.0, 1.0,   
      "base color green " },
   { NUM|FLO,   "Cbaseblue",      0.50,  0.0, 1.0,   
      "base color blue " },
   { NUM|FLO,   "Cmottlered",       0.50,  0.0, 1.0,   
      "mottle color red " },
   { NUM|FLO,   "Cmottlegreen",      0.50,  0.0, 1.0,   
      "mottle color green " },
   { NUM|FLO,   "Cmottleblue",      0.50,  0.0, 1.0,   
      "mottle color blue " },
   { NUM|FLO,   "Cedgered",            0.50,  0.0, 1.0,   
      "edge color red " },
   { NUM|FLO,   "Cedgegreen",      0.50,  0.0, 1.0,   
      "edge color green " },
   { NUM|FLO,   "Cedgeblue",      0.50,  0.0, 1.0,   
      "edge color blue " },
   { NUM|FLO,   "Cspeckred",       0.50,  0.0, 1.0,   
      "speck color red " },
   { NUM|FLO,   "Cspeckgreen",      0.50,  0.0, 1.0,   
      "speck color green " },
   { NUM|FLO,   "Cspeckblue",      0.50,  0.0, 1.0,   
      "speck color blue " }
};

/* The cast struct is for input in the main doit function
   Varstr and Cast must have the same variables in the same order */

typedef struct Cast {
   float stilespacing;
   float ttilespacing;
   float groovewidth;
   float grooveheight;
   float groovedepth;
   float edgevary;
   float mottling;
   float speckly;
   float mottlefreq;
   float varyhue;
   float varysat;
   float varylum;
   float varynormal;
   float Cbasered;
   float Cbasegreen;
   float Cbaseblue;
   float Cmottlered;
   float Cmottlegreen;
   float Cmottleblue;
   float Cedgered;
   float Cedgegreen;
   float Cedgeblue;
   float Cspeckred;
   float Cspeckgreen;
   float Cspeckblue;

} Cast;

/* result:
   Intensity, R, G, B, Alpha, nor.x, nor.y, nor.z
 */

float result[8];

/* cfra: the current frame */

float cfra;

int plugin_tex_doit(int, Cast *, float *, float *, float *);


/* ******************** Fixed functions ***************** */

int plugin_tex_getversion(void)
{
   return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but)
{
}

void plugin_init(void)
{
}

/* this function should not be changed: */

void plugin_getinfo(PluginInfo *info)
{
   info->name= name;
   info->stypes= NR_TYPES;
   info->nvars= sizeof(varstr)/sizeof(VarStruct);

   info->snames= stnames[0];
   info->result= result;
   info->cfra= &cfra;
   info->varstr= varstr;

   info->init= plugin_init;
   info->tex_doit=  (TexDoit) plugin_tex_doit;
   info->callback= plugin_but_changed;
}

/* ************************************************************
   GreenMarble
   ************************************************************ */

int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, 
   float *dyt)
{
   float stilespacing = cast->stilespacing;
   float ttilespacing = cast->ttilespacing;
   float groovewidth = cast->groovewidth;
    float grooveheight = cast->grooveheight;
   float groovedepth = cast->groovedepth;
   float edgevary = cast->edgevary;
    float mottling = cast->mottling;
    float speckly = cast->speckly;
   float mottlefreq = cast->mottlefreq;
   float varyhue = cast->varyhue;
    float varysat = cast->varysat;
    float varylum = cast->varylum;
   float varynormal = cast->varynormal;
   float ss, dss, tt, dtt;
   float len;
   float V1[3];
   float x, y, z;
   float theta1, theta2;

   float Cbase[3], Cedge[3], Cmottle[3], Cspeck[3], mortarcolor[3];
   Cbase[0] = cast->Cbasered; 
        Cbase[1] = cast->Cbasegreen; 
        Cbase[2] = cast->Cbaseblue;
   Cmottle[0] = cast->Cmottlered; 
        Cmottle[1] = cast->Cmottlegreen;
        Cmottle[2] =  cast->Cmottleblue;
   Cedge[0] = cast->Cedgered; 
        Cedge[1] = cast->Cedgegreen;
        Cedge[2] = cast->Cedgeblue;
   Cspeck[0] = cast->Cspeckred;
        Cspeck[1] = cast->Cspeckgreen;
        Cspeck[2] = cast->Cspeckblue;
   mortarcolor[0] = mortarcolor[1] = mortarcolor[2] = 0.5;

   ss = texvec[0];
   tt = texvec[1];
   RotN = 0.0;
   ScaleX = 1.0;

   dss = dtt = .000001;

     if (dxt) {
           ds[0]  = dxt[0];
           ds[1]  = dxt[1];
           ds[2]  = dxt[2];
        }
   if (dyt) {
           dt[0] = dyt[0];
           dt[1] = dyt[1];
           dt[2] = dyt[2];
        }
   if (dxt) dss = 3;
   if (dyt) dtt = 3;
   if (dxt && dyt) {
      filterwidthp(ds, dt);

      V1[0] = ds[0]*dt[0];
      V1[1] = ds[1]*dt[1];
      V1[2] = ds[2]*dt[2];
      z = V1[2];
      if ( z < 0) {
         ScaleX = -1;
         rotateZ(M_PI, ds);
         }

      x = ds[0]; y = ds[1];
      len = sqrt(x*x + y*y);
      theta1 = acos(x/len);
      if (y < 0) theta1 = (2*M_PI) - theta1;

      x = dt[0]; y = dt[1];
      len = sqrt(x*x + y*y);
      theta2 = acos(y/len);
         if (x > 0) theta2 = (2*M_PI) - theta2;

       RotN = MIN2(theta1, theta2);
   }

   x = result[5];
   y = result[6];
   z = result[7];

   len = sqrt(x*x + z*z);
   RotY = acos(-z/len);
   if (x > 0) RotY *= -1;

   len = sqrt(y*y + z*z);
   RotX = acos(-z/len);
   if (y < 0) RotX *= -1;

   ceramictiles (result, ss, dss, tt, dtt, mortarcolor, stilespacing, 
        ttilespacing, groovewidth, grooveheight, groovedepth, edgevary, 
        mottling, speckly, mottlefreq, Cbase, Cedge, Cmottle, Cspeck, 
        varyhue, varysat, varylum, varynormal);

   return 3;
}

void
ceramictiles (float *result, float ss, float dss, float tt, float dtt,
                        float mortarcolor[3],
                float stilespacing, float ttilespacing,
                float groovewidth, float grooveheight, float groovedepth,
                float edgevary, float mottling, float speckly, float mottlefreq,
                float Cbase[3], float Cedge[3], float Cmottle[3], 
                float Cspeck[3], float varyhue, float varysat, 
                float varylum, float varynormal)

{

    extern float RotN, RotX, RotY, ScaleX;
    float dQ;
    float disp = .0001;
    float Ntile[3], Q[3], Vs[3], Vt[3];
    float Displace[3], tmp[3];
    float *Ctile, *Ctile2, *Ci;
    float *Nf;
    float Cmortar[3];
    float swhichtile, twhichtile, stile, ttile, intile, tileindex,
        tiledisp, tiledispDs, tiledispDt;

    /*
     * Get a 2-D texture coordinates for the texturing, then
     * Normalize everything so that the tiles are 1x1 units
     */

    ss /= stilespacing;
    dss /= stilespacing;
    tt /= ttilespacing;
    dtt /= ttilespacing;

    /*
     * Find out where in the pattern we are: which tile we're on, and
     * the (stile,ttile) coordinates (both on [0,1]) within our tile.
     */
    intile = tilepattern (ss, tt, dss, dtt,
                                groovewidth, grooveheight,
                                &swhichtile, &twhichtile, &stile, &ttile);

    tileindex = swhichtile + 13*twhichtile;
    /*
     * Displacement: the edges of the tile displace down a bit, as do
     * the grooves between tiles.  Also, add just a little bit of
     * per-tile normal variation to break up reflections.
     */
        tiledisp = smoothpulse (0, .075, 0.925, 1, stile);
        tiledisp *= smoothpulse (0, .075, 0.925, 1, ttile);

        tiledispDs = smoothpulse (0, .075, 0.925, 1, stile+disp);
        tiledispDs *= smoothpulse (0, .075, 0.925, 1, ttile);

        tiledispDt = smoothpulse (0, .075, 0.925, 1, ttile+disp);
        tiledispDt *= smoothpulse (0, .075, 0.925, 1, stile);

        Vs[0] =disp; 
        Vs[1] = 0;
        Vt[0] = 0; 
        Vt[1] = disp;

        Vs[2]=((float) (groovedepth*(tiledispDs - tiledisp)));
        Vt[2]=((float) (groovedepth*(tiledispDt - tiledisp)));
        Displace[0] = Vt[1]*Vs[2] - Vt[2]*Vs[1];
        Displace[1] = Vt[2]*Vs[0] - Vt[0]*Vs[2];
        Displace[2] = Vt[0]*Vs[1] - Vt[1]*Vs[0];
        normalize(Displace);

        rotateZ(-RotN, Displace);
        Displace[0] = Displace[0]*ScaleX;

        tmp[0] = tmp[1] = 0;
        tmp[2] =-1;

        Ntile[0] = 0.15*(cellnoise(tileindex+5) - 0.5);
        Ntile[1] = 0.15*(cellnoise(tileindex+5) - 0.5);
        Ntile[2] = 0.15*(cellnoise(tileindex+5) - 0.5);
        Ntile[0] = Ntile[0]*varynormal;
        Ntile[1] = Ntile[1]*varynormal;
        Ntile[2] = Ntile[2]*varynormal;
        Ntile[0] += Displace[0];
        Ntile[1] += Displace[1];
        Ntile[2] += Displace[2];
        Nf = mix(tmp, Ntile, intile);
        normalize(Nf);

        rotateX(RotX, Nf);
        rotateY(RotY, Nf);

    /*
     * Here's the exciting part -- calculate the color of the spot we're
     * in within the tile.  Then use the tile index to vary its color
     * so every tile looks a little different.
     */
	Ctile2 = tiletexture(tileindex, stile, ttile, dss, dtt,
                               edgevary, mottling, speckly, mottlefreq,
                               Cbase, Cedge, Cmottle, Cspeck);


	Ctile = varyEach (Ctile2, tileindex, varyhue, varysat, varylum);
	free(Ctile2);

    /*
     * Set the color of the mortar between tiles, make it look good by
     * scaling it by some high frequency fBm.
     */

	Cmortar[0] = mortarcolor[0];
	Cmortar[1] = mortarcolor[1];
	Cmortar[2] = mortarcolor[2];
	Q[0] = ss*20;
	Q[1] = tt*20;
	Q[2] = 0;
	dQ = (dss+dtt)/2.0;
	if (intile < 1.0) {
		Cmortar[0] *= smoothstep (0, 1, (.5 + .4 * fBm (Q, dQ, 3, 2, .6)));
		Cmortar[1] *= smoothstep (0, 1, (.5 + .4 * fBm (Q, dQ, 3, 2, .6)));
		Cmortar[2] *= smoothstep (0, 1, (.5 + .4 * fBm (Q, dQ, 3, 2, .6)));
        }

    /*
     * Illumination model
     */
	Ci = MaterialCeramicTiles (Nf, Cmortar, Ctile, intile);
	free(Ctile);

        result[0] = 1.0;
        result[1] = Ci[0];
        result[2] = Ci[1];
        result[3] = Ci[2];
        result[4] = 1.0;
        free(Ci);

        result[5] = Nf[0];
        result[6] = Nf[1];
        result[7] = Nf[2] - result[7];

        free(Nf);
        return;
}

float *tiletexture (float tileindex,
                 float stile, float ttile, float ds, float dt,
               float edgevary, float mottling, float speckly,
               float mottlefreq, float Cbase[3], float Cedge[3], 
               float Cmottle[3], float Cspeck[3])
{
        float* C;
        float noisep[3], ttt[3];
        float mottle, sedgeoffset, tedgeoffset, edgy, speckfreq, specky;
        float dst = MAX2(ds, dt);

	C = Cbase;

        if (mottling > 0) {
                noisep[0] = stile * mottlefreq;
                noisep[1] = ttile * mottlefreq;
                noisep[2] = tileindex * mottlefreq;
                mottle = .2+.6*fBm(noisep, mottlefreq*MAX2(ds,dt), 4, 2, 0.65);
                C = mix(C, Cmottle, CLAMP(mottling*mottle,0,1));
        }

    if (edgevary > 0) {
        ttt[0] = stile*10;
        ttt[1] = ttile*10;
        ttt[2] = tileindex+10;
        sedgeoffset = .05*fBm(ttt, 10*dst, 2, 2, 0.5);
        ttt[0] = stile*10;
        ttt[1] = ttile*10;
        ttt[2] = tileindex - 3;
        tedgeoffset = .05*fBm(ttt, 10*dst, 2, 2, 0.5);
        edgy = 1 - (smoothpulse (.05, .15, .85, .95, stile+sedgeoffset) *
                          smoothpulse (.05, .15, .85, .95, ttile+tedgeoffset));
        C = mix(C, Cedge, edgevary*edgy);
    }

    if (speckly > 0) {
        speckfreq = 7;
        noisep[0] = stile*speckfreq;
        noisep[1] = ttile*speckfreq;
        noisep[2] = tileindex+8;
        specky = filteredsnoise (noisep, speckfreq*dst)+0.5;
        specky = smoothstep (0.55, 0.7, specky);
        C = mix(C, Cspeck, speckly*specky);
    }

    return C;
}

/* Compute the color of a ceramic object.  Like plastic, but use a
 * "glossy" specular term.  We're actually blending between a purely
 * diffuse model for the mortar, and a ceramic model for the tiles,
 * depending on the variable intile.  When in the mortar area, we turn
 * off highlights and reflections.
 */
float *MaterialCeramicTiles (float Nf[3], float Cmortar[3], float Ctile[3],
         float intile)
{
        float *basecolor;
        basecolor = mix(Cmortar, Ctile, intile);
        return basecolor;
}

float *varyEach (float Cin[3], float index, float varyhue,
                        float varysat, float varylum)
{
        float *ttt;
        float Chsl[3];
        float h,s,l;

        ttt = RgbtoHsl(Cin);
        Chsl[0] = ttt[0];
        Chsl[1] = ttt[1];
        Chsl[2] = ttt[2];
        free(ttt);
        h = Chsl[0]; 
        s = Chsl[1]; 
        l = Chsl[2];

        /* Modify Chsl by adding Cvary scaled by 
           our separate h, s, l controls */
        h += varyhue * (cellnoise(index+3)-0.5);
        h = fmod(h, 1);
        if (h < 0) h = h + 1;
        s *= 1 - varysat * (cellnoise(index-14)-0.5);
        l *= 1 - varylum * (cellnoise(index+37)-0.5);
        Chsl[0] = h;
        Chsl[1] = CLAMP(s, 0, 1);
        Chsl[2] = CLAMP(l, 0, 1);
        /* Clamp hsl and transform back to rgb space  */
        return HsltoRgb(Chsl);
}

float *HsltoRgb(float hsl[3])
{
        float *rgb = malloc(sizeof(float)*3);
        float q=0;
	float s, val;
	int i;
        int index1= -1;
        int index2 = -1;

        val = hsl[0]*360.0;
        hsl[0] = val;
        s = (1 - hsl[1])*hsl[2];
        if (hsl[2] == 0)
        {
                rgb[0] = rgb[1] = rgb[2] = 0.0;
                return rgb;
        } 

        rgb[0] = rgb[1] = rgb[2] = s;

        for (i = 0; i < 3; i++)
        {
                if (hsl[0] >= (120*i) && hsl[0] <= 120*(i+1))
                {
                        index1 = i;
                        if (hsl[0] < (120*i)+60) {index2 = index1-1;}
                        else {index2 = index1+1;}
                        q = fabs(((120*i)+60)-hsl[0])/60.0;
                        break;
                }
        }

        rgb[index1] = hsl[2];
        if (index2 < 0) index2 = 2;
        if (index2 > 2) index2 = 0;
        val = s + q*(hsl[2]-s);
        rgb[index2] = val;

        return rgb;
}

float *RgbtoHsl (float c[3])
{
        float *hsl = malloc(sizeof(float) * 3);
        float h, q, s;
	float k = 0;
	float val = 0;
        int mark1, mark2, i;

        s = MIN3(c[0], c[1],c[2]);
        h = MAX3(c[0], c[1],c[2]);
        if (h > 0)      {hsl[1] = 1 - s/h;}
        else {hsl[1] = 0;}
        hsl[2] = h;
        q = 0;
        mark1 = mark2 = 0;
        if (h == s) {hsl[0] = 0.0;}
        else
        {
           q = 0;
           mark1 = 0; mark2 = 0;
           for (i = 0; i < 3; i++)
           {
                if (c[i] == h) {
                        k = (c[i]-s);
                        val = 60 + 120*i;
                        mark1 = i;
                        h = -1;}
                else if (c[i] != s) {
                        q = (c[i]-s);
                        if (h > 0.0) q *= -1;
                        mark2 = i;}
           }
           if (abs(mark1-mark2) == 2) q *= -1;
           val += (q/k)*60;
           val = val/360;
           hsl[0] = val;
        }
        return hsl;
}

/* Given 2-D texture coordinates ss,tt and their filter widths ds, dt,
 * and the width and height of the grooves between tiles (assuming that
 * tile spacing is 1.0), figure out which (integer indexed) tile we are
 * on and what coordinates (on [0,1]) within our individual tile we are
 * shading.
 */

float tilepattern (float ss, float tt, float ds, float dt,
             float groovewidth, float grooveheight,
             float *swhichtile, float *twhichtile,
             float *stile, float *ttile)
{
    swhichtile[0] = floor (ss);
    twhichtile[0] = floor (tt);
    stile[0] = ss - swhichtile[0];
    ttile[0] = tt - twhichtile[0];

    return filteredpulsetrain (groovewidth, 1, ss+groovewidth/2, ds)
             * filteredpulsetrain (grooveheight, 1, tt+grooveheight/2, dt);
}
