/////////////////////////////////////////////////////////////////////////////
// ambocclude.osl
//
// Description:
//
//   Determines the "ambient occlusion" -- how much of the hemisphere
//   at each surface point is occluded by objects in the scene.  The
//   surface color and the output variable unocc are both set to black
//   where completely occluded, white if completely unoccluded.  In
//   addition, the output variable Nunocc is set to the average normal
//   of the unoccluded samples.  The various computations build in the
//   cosine weighting on the hemisphere.
//
//   The main use of this shader is for gathering the unocc and Nunocc
//   into NDC-indexed texture maps in a first pass.  Then, when
//   rendering a later "beauty pass", those maps can be used to scale
//   light intensity, much like in the envlight.gsl shader.
//   
// 
// Parameters:
//   occlusionname - name of the geometry set to trace against.
//   sdbname - overrides the name of the occlusion spatial database.
//      If "" (the default), the database name will be as set by 
//      Attribute("occlusion:spatialdb"), which defaults to "occlusion.sdb".
//   sdbmode - overrides or amends the file reading/writing mode for the
//      occlusion spatial database, as set by appropriate attribs.
//   sdbfilespace - the coordinate system in which to save the sdb disk file.
//   samples - the number of samples to use to estimate occlusion.
//   adaptive - if zero, always use full samples.  If nonzero, use adaptive
//      sampling.  Default is 1, higher is more aggressively adaptive,
//      lower tends to use full sampling more frequently.
//   maxerror - allowable error for interpolation (versus more expensive
//      resampling of the hemisphere).  If <0, uses the value of
//      Attribute("occlusion:maxerror").
//   maxpixeldist - max allowable distance of influence for interpolation.
//      If <0, uses the value of Attribute("occlusion:maxpixeldist").
//   maxhitdist - causes ray hits farther than this distance to be ignored
//      by the occlusion test.
//   bias - ignores ray hits closer than this amount, handy for preventing
//      incorrect self-intersections due to numeric tolerance problems.
//      If bias < 0, it'll use the value set by Attribute "shadow:bias".
//   falloff - if 0, all ray hits are equally occluding.  If nonzero, the
//      influence of occluders falls off with distance according to
//      falloffmode.
//   falloffmode - if 0, occlusion is exp(-falloff/dist);  if 1, occlusion
//      is pow (1-dist/maxhitdist, falloff).
//   twosided - if zero, occlusion will always sample the hemisphere dictated
//        by the surface normal, which assumes that your models all have
//        normals correctly facing the "outside" and that the camera never
//        views the "back" of the surfaces.  If twosided is nonzero (default),
//        occlusion will be computed for the camera-facing side of the surface.
//   writeC - if nonzero, will write the occlusion info to C, otherwise will
//        leave C alone.
//   transparency - transparency input, used only if use_transparence 
//        is nonzero
//   use_transparency - if nonzero, sets opacity=1-transparency.  Otherwise
//        leaves opacity alone.
//
// Output parameters:
//   unocc - contains the portion (0-1) of the hemisphere above each point
//       that is unoccluded.
//   ambocc - same as unocc, but a color
//   Nunocc - contains the average unoccluded direction.
//
/////////////////////////////////////////////////////////////////////////////
// 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)
/////////////////////////////////////////////////////////////////////////////



surface
ambocclude 
    [[ string description = "Ambient occlusion",
       string opaqueparams = "transparency" ]]
(
    string occlusionname = ""
        [[ string description = "Geometry set for occlusion" ]],
    string sdbname = ""
        [[ string description = "Spatial database name for occlusion cache" ]],
    string sdbmode = ""
        [[ string description = "File mode for occlusion cache spatial database",
           string UItype = "enum:,r,w,rw,ro,wo" ]],
    string sdbfilespace = "world"
        [[ string description = "Coordinate system for storing the sdb file",
           string UItype = "coordsys" ]],
    float samples = 64
        [[ string description = "Number of hemispherical samples",
           string UItype = "int", float UImin = 1 ]],
    float adaptive = 1
        [[ string description = "Use adaptive sampling of hemisphere",
           string UItype = "bool" ]],
    float maxerror = -1
        [[ string description = "Error metric (-1 for global default)",
           float UImax = 1 ]],
    float maxpixeldist = -1
        [[ string description = "Max pixel distance between occlusion samples (-1 for global default)" ]],
    float bias = -1
        [[ string description = "Bias - minimum distance for ray hits (-1 for global default)",
           float UImin = 0 ]],
    float maxhitdist = 1.0e6
        [[ string description = "Max distance for ray hits",
           float UImin = 0 ]],
    float falloff = 0
        [[ string description = "Falloff exponent of occlusion effect (0 = none)" ]],
    float falloffmode = 0
        [[ string description = "Alternate falloff mode",
           string UItype="bool" ]],
    float gain = 1
        [[ string description = "Occlusion gain" ]],
    float gamma = 1
        [[ string description = "Occlusion gamma" ]],
    float twosided = 1
        [[ string description = "Two-sided (occlusion flips normal to face camera)",
           string UItype = "bool" ]],
    float writeC = 1
        [[ string description = "Write occlusion to C if nonzero",
           string UItype = "bool" ]],
    color transparency = 0
        [[ string description = "Transparency of surface" ]],
    float use_transparency = 0
        [[ string description = "Use the transparency flag" ]],
    output float unocc = 1
        [[ string description = "Output: unoccluded portion of hemisphere" ]],
    output color ambocc = 1
        [[ string description = "Output: unoccluded portion of hemisphere" ]],
    output normal Nunocc = 0
        [[ string description = "Output: average unoccluded normal" ]]
  )
{
    if (occlusionname) {
        normal Nf = normalize(N);
        if (twosided != 0)
            Nf = faceforward (Nf, I);
        unocc = 1 - gain * occlusion (P, Nf,
							   "traceset", occlusionname, "coneangle", M_PI_2,
							   "environmentdir", Nunocc,
                               "samples", (int)samples, "maxerror", maxerror,
                               "maxpixeldist", maxpixeldist,
                               "maxdist", maxhitdist, "mindist", bias,
                               "falloff", falloff, "falloffmode", (int)falloffmode /*,
                               "adaptive", adaptive, 
                               "filename", sdbname, "filemode", sdbmode,
                               "filespace", sdbfilespace*/);
        unocc = pow (clamp (unocc, 0, 1), gamma);
        ambocc = unocc;
    }
    if (use_transparency)
        opacity = 1 - transparency;
    ambocc *= opacity;
    if (writeC)
        C = ambocc;
}
