/************************************************************
 *   renamed PSbarnacle.sl for RMR -- tal 3/6/03
 * file: barnacle.sl
 * author: Peter Stuart
 * date: 11/3/2002
 * $Id: PSbarnacle.sl,v 1.1 2003/03/07 04:01:09 tal Exp $
 * description: Shader to produce a bunch of barnacles on a 
 * wood-like surface.
 *
 * ---- surface parameters ----------
 * max_height: height of barnacles. Adjust based on frequency.
 * ncones: number of bulges in barnacle.
 * spikiness: length of spikes on top ridge of barnacle
 * sfreq, tfreq: number of barnacles in s and t
 * sparse: control sparseness of barnacles across surface
 * variation: controls how sparseness parameter varies
 * seed: controls random attributes
 * Ka: ambient lighting attribute
 ************************************************************/

#include "rmannotes.sl"
#include "PSshapes.h"
#include "PSxforms.h"

/* ---- macro to help seed random numbers ------
 * it serves two purposes:
 *  - allows easy searching to ensure non-repeating numbers
 *  - automatically uses seed
 */
#define RN(x) (seed+(x)+0.5)

/* ---- function to create random transformation -----
 * scale_min, scale_max: range of scaling
 * rot_min, rot_max: range of rotation
 * seed: controls transformation
 * row, col: row and column of current tile
 * s, t: current position
 * ss, tt: returned position
 */
void rand_xform(float scale_min, scale_max;
		float rot_min, rot_max;
		float seed;
		float row, col, s, t; output float ss, tt;)
{
  float scale1 = udn2(col+RN(91224), row+RN(11224), scale_min, scale_max);
  float scale2 = udn2(col+RN(99191), row+RN(19191), scale_min, scale_max);
  xform(scale1, scale2,
	udn2(col+RN(52219), row+RN(42219), scale1/2-0.5, 0.5-scale1/2),
	udn2(col+RN(3287), row+RN(4287), scale2/2-0.5, 0.5-scale2/2),
	udn2(col+RN(69), row+RN(74), rot_min, rot_max),
	s, t, ss, tt);
}

/* ---- function to draw barnacle ------
 * ncones: number of bulges
 * spikiness: changes the height of the spikes
 * seed: controls random attributes
 * s, t: current position
 * opacity: returned mask
 * disp: returned displacement
 */
color draw_barnacle(uniform float ncones;
		    float spikiness;
		    float minrad, maxrad, fuzz;
		    float seed;
		    float s, t;
		    output color opacity;
		    output float disp;)
{
  uniform float period = 2*PI*ncones;

  color layer_opac, layer_color, surface_color;
  float layer_disp;
  float bot_rad, top_rad;
  float r, theta;
  float ss, tt;

  ss = xtranslate(0.5, s);
  tt = xtranslate(0.5, t);
  topolar2d(ss, tt, r, theta);


  surface_color = 0;
  layer_color = 0;
  layer_disp = 0;
  opacity = 0;
  disp = 0;

  /* repeat noise along theta to avoid breaks */
  bot_rad = maxrad +
    maxrad*abs(pnoise((theta*ncones/(2*PI)) + RN(98), 2*PI*ncones)*2 - 1);
  top_rad = minrad +
    minrad*abs(pnoise((theta*ncones/(2*PI)) + RN(100), 2*PI*ncones)*2 - 1);

  layer_opac = draw_cone(point(0.5,0.5,0), bot_rad, top_rad, fuzz, s, t);

  /********** displacement *******************/

  /* distort cone to look like several cones */
  disp = comp(layer_opac, 0) + comp(layer_opac, 0) *
    spikiness*abs(snoise((theta*ncones)/(2*PI) + RN(102)));

  /* ridges */
  disp += comp(layer_opac, 0) *
    0.5*abs(snoise(r*10/bot_rad + RN(106)));

  /* subtract out the middle, so it looks like mini volcano */
  disp *= 1-(draw_circle(point(0.5,0.5,0), minrad*2, fuzz, s, t));

  /* add critter mound */
  disp = max(disp, cos(clamp(r/maxrad*0.7*PI, 0, PI)));

  /* take out lip */
  disp -= minrad*(draw_line(point(0.5,0.5+minrad*2,0),
			    point(0.5,0.5-minrad*2,0),
			    fuzz, minrad/3, s, t));

  /*********** outside color *************************/  

  layer_color = color "hsv" (udn(RN(8007), 0.1, 0.2),
			     udn(RN(8009), 0.1, 0.3),
			     udn(RN(8011), 0.8, 1));
  layer_opac = draw_circle(point(0.5,0.5,0), bot_rad-fuzz, fuzz, s, t);
  surface_color = blend(surface_color, layer_color, layer_opac);

  opacity = layer_opac;

  /*********** inside color *************************/

  layer_color = color "hsv" (udn(RN(8014), 0.1, 0.2),
			     udn(RN(8017), 0.1, 0.3),
			     udn(RN(8019), 0.4, 0.6));
  layer_opac = 1-draw_circle(point(0.5,0.5,0), minrad*2, minrad*2, s, t);
  surface_color = blend(surface_color, layer_color, layer_opac);

  return surface_color;
}

surface PSbarnacle(float max_height=0.2, ncones=5, spikiness=0.5;
		 float sfreq=20, tfreq=10;
		 float sparse=1, variation=5;
		 float seed = 1299;
		 float Ka = 0.1;)
{
  color layer_opac, layer_color, surface_color;
  float layer_disp, surface_disp;
  color barn_opac, barn_color;
  float barn_disp;
  float ss, tt, xs, xt;
  float row, col, bomb;
  float true_row, true_col;
  float density;

  float barnrad1 = 0.5;
  float barnrad2 = 0.1;

  barn_color = 0;
  barn_disp = 0;
  barn_opac = 0;
  surface_disp = 0;
  surface_color = 0;

  /********* base **********************************/

  layer_color = Cs;
  layer_opac = float noise((t+snoise(s*40 + RN(44251)))*20 + RN(6672));

  normal Nf = faceforward (normalize(N),I);
  layer_color = layer_color * (Ka*ambient() + 0.5*diffuse(Nf))
    + 0.8*layer_opac*specular(Nf, -normalize(I), 0.05);

  surface_color = blend(surface_color, layer_color, layer_opac);

  /********** barnacles ****************************/

  true_col = whichtile(s, sfreq);
  true_row = whichtile(t, tfreq);

  /* take care of barnacle moving into other regions */
  float i, j;
  for (i=-1; i<=1; i+=1) {
    for (j=-1; j<=1; j+=1) {

      ss = repeat(s, sfreq) - i;
      tt = repeat(t, tfreq) - j;
      col = mod((true_col + i), sfreq);
      row = mod((true_row + j), tfreq);

      bomb = noise(col+RN(199209), row+RN(76721));

      ss += udn(bomb+RN(2981), -1.5, 1.5);
      tt += udn(bomb+RN(651511), -1.5, 1.5);

      /* barnacle density */
      density = 1-sparse * 
	smoothstep(0.25, 0.75, float noise(col*variation/sfreq,
					   row*variation/tfreq));

      if (noise(bomb+RN(2989101)) <= density) {

	rand_xform(0.7, 0.9, 0, 2*PI, seed, row, col, ss, tt, xs, xt);
	layer_color = draw_barnacle(ncones, spikiness,
				    barnrad2, barnrad1, 0.05,
				    (seed+RN(299287))*cellnoise(row, col),
				    xs, xt, layer_opac, layer_disp);
      
	barn_color = blend(barn_color, layer_color, layer_opac);
	barn_disp = max(max_height*layer_disp/max(sfreq, tfreq), barn_disp);
	barn_opac = union(layer_opac, barn_opac);
      }
    }
  }

  /* barnacle displacement */
  vector Nn = normalize(N);
  P += Nn * (barn_disp / length(vtransform("shader", Nn)));
  N = calculatenormal(P);

  /* barnacle lighting */
  Nf = faceforward (normalize(N),I);
  barn_color = barn_color * (Ka*ambient() + 0.8*diffuse(Nf))
    + 0.1*specular(Nf, -normalize(I), 0.5);
  surface_color = blend(surface_color, barn_color, barn_opac);

  Ci = surface_color;
}

