/* PhBWardReflector.C: PhBWardReflector nodes (public source) */

// Ward's specular factor (only specular reflection)
// taken from:  SIGRAPH'92 'Measuring and Modeling Anisotropic Reflection' pp265-272 [5a p268]
// changes:
// - more efficient and correct vector expression for the brdf
// - more efficient sampling + proper normalisation of the pdf

// Warning: the albedo is only approximate. The error can be quite large
// for grazing incident directons. This is because part of the light is
// reflected under the surface according to the model.
// The approximation is better for normal incidence (inDir = Z) and
// for small parameters standardDeviationX and ...Y.

#include "PhBWardReflector.H"

namespace xrml {

void PhBWardReflector::render(void)
{
  if (standardDeviationX < EPSILON) standardDeviationX = EPSILON;
  if (standardDeviationY < EPSILON) standardDeviationY = EPSILON;

  // TODO: precompute constants needed below (constants = everything that
  // does not depend on directions).
}

float PhBWardReflector::albedo(const Vec3& inDir,
			       const complex& IndexIn,
			       const complex& IndexOut,
			       int modes)
{
  return (!(modes & SM_SPECULAR_REFLECTION))
    ? 0.
    : fabs(inDir.z);
}

// This sampling technique and its normalisation are the
// result of "conventional Monte Carlo integral conversions".
Vec3 PhBWardReflector::sample(const Vec3& inDir,
			      const complex& IndexIn,
			      const complex& IndexOut,
			      float xi1, float xi2,
			      int modes)
{
  double r = (xi1 > EPSILON*EPSILON) ? sqrt(-log(xi1)) : HUGE;
  double psi = xi2 * (2.*M_PI);
  double x = standardDeviationX * r * cos(psi);
  double y = standardDeviationY * r * sin(psi);
  double z = 1. / sqrt(x*x + y*y + 1.);	// choose z so h will be normalised
  x *= z; y *= z;
  Vec3 h = Vec3(x, y, z);		// halfway vector (microfacet normal)
  return h * (2. * (h & inDir)) - inDir;// mirror inDir w.r.t. h
}


float PhBWardReflector::eval(const Vec3& inDir,
			     const Vec3& outDir,
			     const complex& IndexIn,
			     const complex& IndexOut,
			     int modes,
			     float *pdf)
{
  if (pdf) *pdf = 0.;   // initialisation

  if (!(modes & SM_SPECULAR_REFLECTION))
    // only specular reflection in this model
    return 0.;

  // calculate Wards eliptic Gaussian
  Vec3 h = (Vec3)inDir + (Vec3)outDir;	// halfway vector (not normalised).
  double xfac = (h.x / (h.z * standardDeviationX));	// normalisation of h doesn't matter.
  double yfac = (h.y / (h.z * standardDeviationY));
  double val = exp( - (xfac*xfac + yfac*yfac) );

  // normalisation
  double N = M_PI * standardDeviationX * standardDeviationY;

  // For normalised h, one division more, by the square length of the unnormalised
  // halfway vector, would be necessary.
  if (pdf) *pdf = val * fabs((h & outDir) / (h.z*h.z*h.z)) / N;

  if ((outDir.z > 0.) != (inDir.z > 0.))
    // refracted direction
    // pdf is not 0: The sampling routine above will yield directions in
    // a full sphere. Though the probabbility of getting a direction on
    // the wrong side of the surface, is very low, it is not zero.
    return 0.;

  // check if vector doesn't lie in plane
  if ((fabs(inDir.z) < EPSILON) || (fabs(outDir.z) < EPSILON))
    return 0;

  double normalisation = sqrt( inDir.z * outDir.z ) * 4 * N;
  return val / normalisation;
}

}  // namespace xrml
