/////////////////////////////////////////////////////////////////////////////
// oakplank.sl
//
// help: makes procedural varnished wood planks.  The planks
//    are projected onto the x-y plane, with the length aligned with
//    the y axis.  The subpattern within each individual plank is just
//    a shifted version of the oaktexture function from oak.h.
//   
// Parameters for the coordinate mapping: 
//   shadingspace - space in which the pattern is laid out
//   shadingfreq - overall scaling factor for the pattern
//   Pref - if supplied, gives the reference pose
//
// Parameters for the pattern of the plank structure: 
//   plankwidth, planklength - size of the planks
//   groovewidth, grooveheight - width of the grooves between planks
//   Cgroove - color of the grooves between the planks
//   groovedepth - how far down do the grooves displace?
//   edgewidth - how close to the plank border does the wood start to curl?
//   varyhue, varysat, varylum - control plank-to-plank color variation
// 
// Parameters for the color and pattern of the wood grain: 
//   Clightwood - the light, "background" wood color
//   Cdarkwood - the darker color in the ring/grain
//   ringfreq - mean frequency of ring spacing
//   ringunevenness - 0=equally spaced rings, larger is unequally spaced
//   grainfreq - frequency of the fine grain
//   ringnoise, ringnoisefreq - general warping of the domain
//   trunkwobble, trunkwobblefreq - controls noise which wobbles the
//       axis of the trunk so that it's not perfectly on the z axis.
//   angularwobble, angularwobblefreq - warping indexed by angle about
//       the z axis.
//   ringy, grainy - overall scale on the degree to which rings and
//       grain are weighted.  0 turns one off, 1 makes full effect.
//   divotdepth - depth (in shader units) of the displacement due to
//       ring or grain.
//   Km - overall scaling of the bumpiness
//
// Parameters for illumination model:
//   Ka, Kd, Ks, roughness - the usual meaning
//   Kr, blur, eta - reflection parameters for the tile
//   envname, envspace - environment mapping controls
//   rayjitter, raysamples - ray tracing controls
//   varnishlump, arnishlumpfreq - amp & freq of lumpiness in the varnish
//
// Much of this is adapted from Apodaca & Gritz, "Advanced RenderMan",
// Morgan-Kaufmann 1999.
//
/////////////////////////////////////////////////////////////////////////////
// Copyright 2004 NVIDIA Corporation.  All Rights Reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// 
// * Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA nor the names of its contributors
//   may be used to endorse or promote products derived from this software
//   without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
// OWNER 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.
//
// (This is the Modified BSD License)
/////////////////////////////////////////////////////////////////////////////


#include "material.h"
#include "patterns.h"
#include "oak.h"


// Given 2-D texture coordinates ss,tt, filter widths ds, dt, and the
// width and height of the grooves between tiles, figure out which
// (integer indexed) plank we are on and what coordinates within our
// individual plank we are shading.
float
plankpattern (float ss, float tt, float ds, float dt,
	      float plankwidth, float planklength,
	      float groovewidth, float grooveheight,
	      output float swhichplank, output float twhichplank,
	      output float splank, output float tplank)
{
    // Find which s plank we're on and our s coordinate within it
    swhichplank = floor (ss/plankwidth);
    splank = ss - swhichplank * plankwidth;
    // Shift in t a random amount for each plank column
    float newt = tt + planklength * cellnoise(swhichplank);
    // Find which t plank we're on and our t coordinate within it
    twhichplank = floor (newt/planklength);
    tplank = newt - twhichplank*planklength;
    // Calculate our "in-plank" value by multiplying two perpendicular
    // filtered pulsetrain functions.
    return filteredpulsetrain (groovewidth, plankwidth, ss+groovewidth/2, ds)
       * filteredpulsetrain (grooveheight, planklength,
			     newt+grooveheight/2, dt);
}



surface
oakplank
    [[ string help = "Oak planks" ]]
(
    float Ka = 1
        [[  string help = "Ambient scaling",
            float min = 0, float max = 1 ]],
    float Kd = 1
        [[  string help = "Diffuse scaling",
            float min = 0, float max = 1 ]],
    float Ks = .75
        [[  string help = "Specular scaling",
            float min = 0, float max = 1 ]],
    float roughness = 0.1
        [[  string help = "Surface roughness (specular highlight size)",
            float min = 0, float UImax = 1 ]],

    float Kr = 1
        [[  string help = "Reflectivity / Environment scaling",
            string UIlabel = "Reflectivity",
            float min = 0, float max = 1 ]],
    float eta = 1.5
        [[  string help = "Index of refraction",
            float min = 1, float max = 2 ]],
    float blur = 0
        [[  string help = "Environment blur",
            string UIenabler = "Kr",
            float min = 0, float max = 1 ]],
    string envname = ""
        [[  string help = "Map or geometry set name for reflections" ]],
    string envspace = "world"
        [[  string help = "Coordinate system for environment map lookup" ]],
    int samples = 1
        [[  string help = "Environment samples (for ray tracing)",
            float min = 1, float max = 64 ]],

    float divotdepth = 0.012
        [[ string help = "Divot depth for rings and grain" ]],
    color Clightwood = color (.5, .2, .067)
        [[ string help = "Light wood color" ]],
    color Cdarkwood = color(0.15, 0.077, 0.028)
        [[ string help = "Dark wood color" ]],

    float ringy = 1
        [[ string help = "Ring amplitude",
           float min = 0, string UIgroupbegin = "Rings" ]],
    float ringfreq = 8
        [[ string help = "Ring frequency" ]],
    float ringunevenness = 0.5
        [[ string help = "Ring unevenness" ]],
    float ringnoise = 0.02
        [[ string help = "Ring noise" ]],
    float ringnoisefreq = 1
        [[ string help = "Ring noise frequency" ]],
    float trunkwobble = 0.15
        [[ string help = "Trunk wobble" ]],
    float trunkwobblefreq = 0.025
        [[ string help = "Trunk wobble frequency" ]],
    float angularwobble = 1
        [[ string help = "Angular wobble" ]],
    float angularwobblefreq = 1.5
        [[ string help = "Angular wobble frequency" ]],

    float grainy = 1
        [[ string help = "Grain amplitude" ]],
    float grainfreq = 25
        [[ string help = "Grain frequency" ]],

    float plankwidth = 2
        [[ string help = "Plank width" ]],
    float planklength = 30
        [[ string help = "Plank length" ]],
    float groovewidth = 0.05
        [[ string help = "Groove width" ]],
    float grooveheight = 0.05
        [[ string help = "Groove height" ]],
    float groovedepth = 0.03
        [[ string help = "Groove depth",
           float min = 0  ]],
    float edgewidth = 0.1
        [[ string help = "Edge width",
           float min = 0, float max = 0.5 ]],
    color Cgroove = color(0.02, 0.02, 0.02)
        [[ string help = "Groove color" ]],
    float varyhue = 0.015
        [[ string help = "Plank hue variation",
           float min = 0 ]],
    float varysat = 0.1
        [[ string help = "Plank saturation variation",
           float min = 0  ]],
    float varylum = 0.5
        [[ string help = "Plank luminance variation",
           float min = 0  ]],

    float varnishlump = 0.01
        [[ string help = "Varnish lumpiness" ]],
    float varnishlumpfreq = 0.5
        [[ string help = "Varnish lump frequency" ]],

    float Km = 1
        [[ string help = "Overall displacement amplitude" ]],
    point Pref = P
        [[  string help = "Reference pose (defaults to P)" ]],
    string shadingspace = "shader"
        [[  string help = "Shading space" ]],
    float shadingfreq = 1
        [[  string help = "Shading frequency" ]]
  )
{
    point Pshad = shadingfreq * transform (shadingspace, Pref);
    float dPshad = sqrt (area(Pshad));
    float ss = Pshad[0], tt = Pshad[1], height = Pshad[2];
    float dss = filterwidth(ss), dtt = filterwidth(tt);

    // Find out where in the pattern we are: which plank we're on, and
    // the (splank,tplank) coordinates (both on [0,1]) within our tile.
    float swhichplank, twhichplank, splank, tplank;
    float inplank = plankpattern (ss, tt, dss, dtt, plankwidth, planklength,
				  groovewidth, grooveheight,
				  swhichplank, twhichplank, splank, tplank);
    float plankindex = swhichplank + 13*twhichplank;
    point Ppat = point(splank-0.5,height-0.01*tplank,tplank) 
          + vector(1,5,10)* ((vector)cellnoise(swhichplank,twhichplank) - 0.5);
    float wood = oaktexture (Ppat, dPshad, ringfreq, ringunevenness, grainfreq,
			     ringnoise, ringnoisefreq, trunkwobble,
			     trunkwobblefreq, angularwobble, 
			     angularwobblefreq, ringy, grainy);
    color Cwood = mix (Clightwood, Cdarkwood, wood);
    Cwood = varyEach (Cwood, plankindex, varyhue, varysat, varylum);
    Cwood = mix (Cgroove, Cwood, inplank);

    // Displacement: the edges of the planks displace down a bit, as do
    // the grooves between planks. 
    float edgedisp = smoothpulse (0, edgewidth, plankwidth-edgewidth,
				  plankwidth, splank);
    edgedisp *= smoothpulse (0, edgewidth, planklength-edgewidth,
			     planklength, tplank);
    float disp = -wood*divotdepth + groovedepth*(edgedisp-1);
    disp += varnishlump * filteredsnoise (Pshad*varnishlumpfreq,
					  dPshad*varnishlumpfreq);
    bump ("shader", Km*disp);

    // Illumination model
    // Less specular in the grooves, more specular in the dark wood. 
    normal Nf = faceforward (normalize(N), I);
    float specadjusted = 1 + .3*wood - 0.8*(1-inplank);
    C = MaterialShinyPlastic (Nf, Cwood, Ka, Kd, specadjusted*Ks, roughness,
                              specadjusted*Kr, blur, eta, 1, 
                              envname, envspace, samples);
    C *= opacity;
}
