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

#include "PhBLafortuneReflector.H"
#include "matrix.H"

namespace xrml {

float PhBLafortuneReflector::glossy_threshold = 20;

void PhBLafortuneReflector::render(void)
{
  // needs nothing to do
}

Vec3 PhBLafortuneReflector::MirrorDirection(const Vec3& inDir)
{
  return (Vec3)C * inDir;
}

// approx. albedo
float PhBLafortuneReflector::albedo(const Vec3& inDir,
				const complex& IndexIn,
				const complex& IndexOut,
				int modes)
{
  if (!(modes & SM_REFLECTION))
      return 0.;

  if (!(modes & SM_REFLECTION) ||
      !((n <= glossy_threshold && (modes & SM_GLOSSY_REFLECTION)) ||
	(n > glossy_threshold && (modes & SM_SPECULAR_REFLECTION)))) {
    // no contribution of the requested scattering mode.
    return 0.;
  }

  Vec3 mirdir = MirrorDirection(inDir);
  return fabs(inDir.z) * pow(mirdir.norm(), n) * (2. * M_PI) / (n+2.);
}

Vec3 PhBLafortuneReflector::sample(const Vec3& inDir,
			       const complex& IndexIn,
			       const complex& IndexOut,
			       float xi1, float xi2,
			       int modes)
{
  // rotation transforming the Z axis into the warped mirror
  // direction.
  Vec3 Z = MirrorDirection(inDir).normalize();
  double zz = sqrt(1 - Z.z*Z.z);
  Vec3 X = (zz < EPSILON)
    ? Vec3(1.,0.,0.)
    : Vec3(Z.y/zz, -Z.x/zz, 0.);
  Vec3 Y = Z ^ X;
  Mat3 rot(X,Y,Z);

  double phi = 2. * M_PI * xi1;
  double cos_phi = cos(phi);
  double sin_phi = sin(phi);
  double cos_theta = pow(xi2, 1./(n+1.));
  double sin_theta = sqrt(1. - cos_theta*cos_theta);
  return Vec3(cos_phi * sin_theta,
	      sin_phi * sin_theta,
	      cos_theta) * rot;
}

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

  if (!(modes & SM_REFLECTION) ||
      !((n <= glossy_threshold && (modes & SM_GLOSSY_REFLECTION)) ||
	(n > glossy_threshold && (modes & SM_SPECULAR_REFLECTION)))) {
    // no contribution of the requested scattering mode.
    return 0.;
  }

  Vec3 mirdir = MirrorDirection(inDir); // not normalized!
  double cosa = mirdir & outDir;  // one term of formula 8 in Lafortunes SIGGRAPH'97 paper
  if (cosa <= 0.)	// angle >= 90 degrees
    // pdf is 0: such outgoing rays cannot result from the sampling method
    // above.
    return 0.;

  double val = pow(cosa, n); // no normalisation here
  if (pdf) *pdf = val / pow(mirdir.norm(), n) * (n+1.) / (2. * M_PI); // with normalised mirror direction here

  if ((outDir.z > 0.) != (inDir.z > 0.))
    // refracted direction
    // pdf is not 0: the sampling method above can yield rays
    // leaving the wrong side of the surface!
    return 0.;
  return val;
}

}  // namespace xrml
