/* PhBAshikminReflector.C: PhBAshikminReflector nodes (public source) */
//
// Ashikmin Reflector aka Shirley reflector
// From: "Realistic Ray Tracing", Peter Shirley 2000
// ISBN: 1-56881-110-1
//

#include "PhBAshikminReflector.H"
#include "vector.H"

namespace xrml {
  
float PhBAshikminReflector::glossy_threshold = 20;
  
void PhBAshikminReflector::render(void)
{
  // nothing
}

float PhBAshikminReflector::albedo(const Vec3& inDir,
				   const complex& IndexIn,
				   const complex& IndexOut,
				   int modes)
{
  double sharpness = (nu < nv) ? nu : nv;

  if (!(modes & SM_REFLECTION))
      return 0.;

  if ((sharpness == 0. && (modes & SM_DIFFUSE_REFLECTION)) ||
      (sharpness <= glossy_threshold && (modes & SM_GLOSSY_REFLECTION)) ||
      (sharpness > glossy_threshold && (modes & SM_SPECULAR_REFLECTION)))
    return fabs(inDir.z);
  else
    return 0.;
}

Vec3 PhBAshikminReflector::sample(const Vec3& inDir,
				  const complex& IndexIn,
				  const complex& IndexOut,
				  float xi1, float xi2,
				  int modes)
{
  double theta = atan( sqrt( (nu + 1.) / (nv + 1.) ) * tan( M_PI * xi1 / 2. ) );
  double sinTheta = sin(theta);
  double cosTheta = cos(theta);

  double cosDelta = pow( (1. - xi2), 1. / (nu * cosTheta * cosTheta + nv * sinTheta * sinTheta + 1) );
  double sinDelta = sqrt(1. - cosDelta * cosDelta);

  Vec3 h = Vec3( sinDelta * cosTheta, sinDelta * sinTheta, cosDelta );
  
  return Vec3(-inDir + h * 2. * (inDir & h));
}

float PhBAshikminReflector::eval(const Vec3& inDir,
				 const Vec3& outDir,
				 const complex& IndexIn,
				 const complex& IndexOut,
				 int modes, 
				 float *pdf)
{
  double sharpness = (nu < nv) ? nu : nv;

  if(pdf) *pdf = 0.;

  if (!(modes & SM_REFLECTION))
      return 0.;

  if (!((sharpness == 0. && (modes & SM_DIFFUSE_REFLECTION)) ||
      (sharpness <= glossy_threshold && (modes & SM_GLOSSY_REFLECTION)) ||
      (sharpness > glossy_threshold && (modes & SM_SPECULAR_REFLECTION))))
    return 0.;

    
  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.;
  
  Vec3 h = Vec3(inDir + outDir).normalize();
  double hu = h.x;
  double hv = h.y;
  double hn = h.z;
  double hk1 = h & inDir;
  double hk2 = h & outDir;
  double nk1 = inDir.z;
  double nk2 = outDir.z;

  // check boundary conditions
  double p = 1.;
  if((1. - (hn*hn)) > EPSILON) p = pow(hn, (nu*hu*hu + nv*hv*hv) / (1. - hn*hn) );

  double c = sqrt( (nu + 1.) * (nv + 1.) ) / (8. * M_PI);
  double max = (nk1 > nk2) ? nk1 : nk2;

  if(pdf)
    { 
      if(hk1 < EPSILON) *pdf = 0.;
      else *pdf = (float)(c * p / hk1);
    }
  
  return( (float)((c * p) / (hk2 * max)) );
}
  
}  // namespace xrml
