/////////////////////////////////////////////////////////////////////////////
// envlight.osl
//
// Description:
//   A light that illuminates based on an environment map, taking
//   local occlusion into account.  Works best if the environment map is
//   high dynamic range.
//
//   If the occlusionmap and normalmap are set, then those files will
//   be used to look up the occlusion and normal.  It's assumed that
//   these are NDC space maps that were computed on a previous
//   rendering pass, by a shader such as ambocclude.gsl.
//
//   If no occlusionmap is supplied but occlusionname contains the
//   name of a geometry set, ray tracing will be used to compute the
//   occlusion.
// 
// Parameters:
//   intensity - scale the brightness of the map lookup
//   lightcolor - color to use if no environment map is supplied
//   Kocclusion - controls how much the occlusion map is considered.  A
//      value of 1 completely weights the light by the unocclusion amount,
//      whereas 0 does not even consider the occlusion map.
//   __nonspecular - allows the light to not show up in specular calls, as
//      you'd want if you were using this for a diffuse fill light.
//
//   envname - name of the environment map
//   envspace - space in which to look up the direction
//   envrad - radius of the environment map (0 indicates infinite)
//   blur - how much to blur the map
//
//   occlusionmap - name of occlusion map (if it's looked up rather than
//      computed).
//   normalmap - name of normal direction map (if it's looked up rather
//      than computed).
//
//   occlusionname - name of the geometry set to trace against (if it's
//      actively traced rather than looked up from a map)
//   sdbname - overrides the name of the occlusion spatial database.
//      If "" (the default), the database name will be as set by 
//      Attribute("occlusion:spatialdb").
//   sdbmode - overrides or amends the file reading/writing mode for the
//      occlusion spatial database, as set by appropriate attribs.
//   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).
//
/////////////////////////////////////////////////////////////////////////////
// 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)
/////////////////////////////////////////////////////////////////////////////



light
envlight
    [[ string description = "light source that looks up from an environment map" ]]
(
    float intensity = 1
        [[ string description = "Overall light intensity",
           float UIsoftmin=0 ]],
    color lightcolor = 1
        [[ string description = "Overall light color" ]],

    string envname = ""
        [[ string description = "Environment map name",
           string UItype = "envmap",
           string UIgroupbegin = "Environment" ]],
    string envspace = "world"
        [[ string description = "Coordinate system for environment map lookup",
           string UIlabel = "Environment space",
           string UItype = "coordsys" ]],
    float envrad = 0
        [[ string description = "Environment sphere radius for env map lookup",
           float UImin = 0 ]],
    float blur = 0.5
        [[ string description = "Environment blur",
           float UImin = 0, float UIsoftmax = 1,
           string UIgroupend = "Environment" ]],

    string occlusionname = ""
        [[ string description = "Geometry set for occlusion",
           string UItype = "geometryset",
           string UIgroupbegin = "Occlusion" ]],
    float bias = -1
        [[ string description = "Bias - minimum distance for ray hits",
           float UImin = 0 ]],
    float maxhitdist = 1.0e6
        [[ string description = "Max distance for ray hits",
           float UImin = 0 ]],
    float samples = 16
        [[ 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 falloff = 0
        [[ string description = "Falloff exponent of occlusion effect (0 = none)" ]],
    float falloffmode = 0
        [[ string description = "Alternate falloff mode",
           string UItype="bool" ]],
    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,wo", string UIenabler = "sdbname" ]],
    string sdbfilespace = "world"
        [[ string description = "Coordinate system for storing the occlusion cache sdb file",
           string UItype = "coordsys" ]],
    string occlusionmap = ""
        [[ string description = "Occlusion map name (if not computed)" ]],
    string normalmap = ""
        [[ string description = "Normal map name (if not computed)" ]],
    float Kocclusion = 1
        [[ string description = "Amount occlusion is considered",
           float UImin = 0, float UImax = 1,
           string UIgroupend = "Occlusion" ]],

    float __nonspecular = 1
        [[ string description = "Non-specular" ]]
  )
{
    normal Nunocc;
    float a;

    if (occlusionmap != "") {
        // An occlusion map was supplied - look up using "NDC" coords
        point PNDC = transform ("NDC", P);
        float x = PNDC[0], y = PNDC[1];
        a = texture (occlusionmap, x, y);
        if (normalmap)
            Nunocc = (normal) texture (normalmap, x, y);
        else Nunocc = N;
    } else if (occlusionname != "") {
        // No occlusion map given, but a geometryset was passed --
        // compute occlusion
        a = 1 - occlusion (Ps, normalize(N),
						   "traceset", occlusionname, "coneangle", M_PI_2,
						   "environmentdir", Nunocc,
                           "samples", (int)samples, "maxerror", maxerror,
                           "maxpixeldist", maxpixeldist,
                           "maxdist", maxhitdist, "bias", bias,
                           "falloff", falloff, "falloffmode", (int)falloffmode/*,
                           "adaptive", adaptive, 
                           "filename", sdbname, "filemode", sdbmode,
                           "filespace", sdbfilespace*/);
    } else {
        // Neither an occlusion map nor an occlusion geomset was supplied.
        // Do not consider occlusion at all.
        a = 1;
        Nunocc = N;
    }
    a = mix (1, a, Kocclusion);

    emit (-Nunocc, 0) {
		setmessage ("__nonspecular", __nonspecular);
        Cl = lightcolor * intensity * a;
		if (envname) {
			Cl *= environment (envname, Nunocc, "space", envspace,
										   "radius", envrad, "blur", blur);
        }
    }
}
