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

#include "PhBHenyeyGreensteinPhaseFunction.H"

namespace xrml {
void PhBHenyeyGreensteinPhaseFunction::render(void)
{
  // none needed
}

float PhBHenyeyGreensteinPhaseFunction::albedo(const Vec3& inDir,
					       int modes)
{
  return(1. / (4 * M_PI));
}

Vec3 PhBHenyeyGreensteinPhaseFunction::sample(const Vec3& inDir,
					      float xi1, float xi2,
					      int modes)
{
  // sampling as in Pat Hanrahan's "Reflection from Layered Surfaces due to Subsurface scattering, SIGGRPAH '93
  double g2 = g * g;
  double f = 1 - g2 / (1 - g + 2*g*xi1);
  double cosj = 1 / fabs(2*g) * (1 + g2 - f*f);
  double sinj = sqrt(1 - cosj*cosj);
  double cosDelta = cos(2 * M_PI * xi2);
  double sinDelta = sin(2 * M_PI * xi2);
  double cosTheta = inDir.z;
  double sinTheta = sqrt(1 - cosTheta*cosTheta),   isinTheta = 1 / sinTheta;
  Vec3 t;

  if(sinTheta > EPSILON) {
    t = Vec3( (inDir.x * cosDelta * cosTheta - inDir.y * sinDelta) * isinTheta,
		   (inDir.y * cosDelta * cosTheta + inDir.x * sinDelta) * isinTheta,
		   sinTheta);
  }
  else t = Vec3(cosDelta, sinDelta, 0);      // inDir == Z-axis

  return Vec3(inDir * cosj + t * sinj);
}

float PhBHenyeyGreensteinPhaseFunction::eval(const Vec3& inDir,
					     const Vec3& outDir,
					     int modes, 
					     float *pdf)
{
  // Henyey-Greenstein phase function (a = cos j, j = angle between inDir and OutDir):
  //                 
  // P  (g, a) = (1/4pi) * (1 - g^2) / (1 - 2ga + g^2)^1.5
  //  HG
  double a = inDir & outDir;                    // cos j
  double g2 = g * g;
  double div = 1 - 2 * g * a + g2;
  double norm = 1 / (4 * M_PI);                 // normalisation factor
  double value = (1 - g2) * pow(div, -1.5);
  
  if (pdf) *pdf = norm * value;

  return value;
}

}  // namespace xrml
