/* xxdf.c : some general functions regarding edf,brdf,btdf,bsdf */

#include "error.h"
#include "xxdf.h"
#include "vector.h"

/* Compute an approximate Geometric IOR from a complex IOR (cfr. Gr.Gems II, p289) */

float ComplexToGeometricRefractionIndex(REFRACTIONINDEX nc)
{
  float sqrtF, f1,f2;

  f1 = (nc.nr - 1.0); 
  f1 = f1*f1 + nc.ni * nc.ni;

  f2 = (nc.nr + 1.0); 
  f2 = f2*f2 + nc.ni * nc.ni;

  sqrtF = sqrt(f1 / f2);

  return (1.0 + sqrtF) / (1.0 - sqrtF);
}

/* Calculate the ideal reflected ray direction (independent of the brdf) */
VECTOR IdealReflectedDirection(VECTOR *in, VECTOR *normal)
{
  double tmp;
  VECTOR result;

  tmp = 2 * VECTORDOTPRODUCT(*normal, *in);
  VECTORSCALE(tmp, *normal, result);
  VECTORSUBTRACT(*in, result, result);
  VECTORNORMALISE(result);

  return result;
}

DVECTOR DIdealReflectedDirection(DVECTOR *in, DVECTOR *normal)
{
  double tmp;
  DVECTOR result;

  tmp = 2.0 * VECTORDOTPRODUCT(*normal, *in);
  VECTORSCALE(tmp, *normal, result);
  VECTORSUBTRACT(*in, result, result);
  VECTORNORMALISE(result);

  return result;
}



/* Calculate the perfect refracted ray direction.
 * Sets totalInternalReflection to TRUE or FALSE accordingly.
 * Cfr. An Introduction to RayTracing (Glassner)
 */

VECTOR IdealRefractedDirection(VECTOR *in, VECTOR *normal, 
				   REFRACTIONINDEX inIndex, 
				   REFRACTIONINDEX outIndex, 
				   int *totalInternalReflection)
{
  double Ci,Ct, Ct2;
  double refractionIndex;
  /*  float nr, ni; */
  double normalScale;
  VECTOR result;

  /* !! Only real part of n for now */

  refractionIndex = inIndex.nr / outIndex.nr;

  Ci = - VECTORDOTPRODUCT(*in, *normal);
  Ct2 = 1 + refractionIndex * refractionIndex * ( Ci * Ci - 1);

  if(Ct2 < 0)
  {
    *totalInternalReflection = TRUE;
    return(IdealReflectedDirection(in, normal));
  }
  *totalInternalReflection = FALSE;

  Ct = sqrt(Ct2);

  normalScale = refractionIndex * Ci - Ct;

  VECTORSCALE(refractionIndex, *in, result);
  VECTORADDSCALED(result, normalScale, *normal, result);

  /* Normalise */
  VECTORNORMALISE(result);

  return result;
}


DVECTOR DIdealRefractedDirection(DVECTOR *in, DVECTOR *normal, 
				 REFRACTIONINDEX inIndex, 
				 REFRACTIONINDEX outIndex, 
				 int *totalInternalReflection)
{
  double Ci,Ct, Ct2;
  double refractionIndex;
  /* float nr, ni; */
  double normalScale;
  DVECTOR result;

  /* !! Only real part of n for now */
  refractionIndex = inIndex.nr / outIndex.nr;

  Ci = - VECTORDOTPRODUCT(*in, *normal);
  Ct2 = 1 + refractionIndex * refractionIndex * ( Ci * Ci - 1);

  if(Ct2 < 0)
  {
    *totalInternalReflection = TRUE;
    return(DIdealReflectedDirection(in, normal));
  }
  *totalInternalReflection = FALSE;

  Ct = sqrt(Ct2);

  normalScale = refractionIndex * Ci - Ct;

  VECTORSCALE(refractionIndex, *in, result);
  VECTORADDSCALED(result, normalScale, *normal, result);

  /* Normalise */
  VECTORNORMALISE(result);

  return result;
}

