// ============================================================================
// $Id: hminfo.cpp,v 1.12 2003/10/30 16:36:36 havran Exp $
//
// hminfo.cpp
//
// Licence and usage conditions in November 2003:
// This file can be used and modified only with written permission of
// Vlastimil Havran, Pohodli 27, 57001 Litomysl, the Czech Republic.
// This library is granted to be used by Polina Kondratieva within
// her MPI HIWI job working on BRDFLIB library. The usage condition
// may change in the next version of the library. At the moment,
// the library development is supported by Max-Planck-Institut for
// Informatik, Stuhlsatzenhausweg 85, 66123 Saarbruecken Germany
// See WWW: http://www.mpi-sb.mpg.de
// REPLACEMENT_STRING
//
// Initial coding by Vlastimil Havran + Petr Mladek and Jiri Liska,
// January 2000
//
// Given to BRDFLIB project for the Polina Kondratieva's
// HIWI JOB assignment in November 2003.

// ============================================================================

// BRDFLIB headers
#include "config.h"
#include "hminfo.h"

// Because of _MSC_VER compiler
#undef GetObject

__BEGIN_BRDFLIB_SOURCE

// static variable for accessing constant textures, that do not
// need any mapping
const CMapPointInfo
CHitPointInfo::constMapInfo;

// copy constructor
CHitPointInfo::CHitPointInfo(const CHitPointInfo &info)
{
  // copy the values, no deep copy is needed for most of parameters
  memcpy((void*)this, (void*)&info, sizeof(CHitPointInfo));
}

// assignment operator
CHitPointInfo&
CHitPointInfo::operator=(const CHitPointInfo &info)
{
  if (this == &info)
    return *this;

  // copy the values, no deep copy is needed for most of parameters
  memcpy((void*)this, (void*)&info, sizeof(CHitPointInfo));

  return *this;
}

const CVector2D&
CHitPointInfo::GetTexCoords() const
{
  if(!texCoordsValid) {
	WARNING_INFO("Texture coordinates are not valid\n");
  }
  
  return texCoords;
}

const CVector3D&
CHitPointInfo::GetNormal() const
{
#ifdef _DEBUG
  if (point.x == MAXFLOAT) {
    FATAL << " the point was not extrapolated, use hitInfo.Extrap()"
	  << " before CHitPointInfo::GetNormal() is called.\n";
    FATAL_ABORT;
  }
#endif

 // Commented by KP
 /* if (!normalValid) {
    assert(ray);
    // !!!!
    // This function must be implemented by the user of this library
    // accessing some geometric object data.
    // !!!!
    // temporary version
    ((CVector3D&)normal).SetValue(0.0, 0.0, 1.0);
    // !!!!

    // compute the dot product
    (float&)dotProd = DotProd(normal, ray->GetDir());
    assert(dotProd <=  1.00001);
    assert(dotProd >= -1.00001);
    (bool&)normalValid = true;
  }
  else {
    (float&)dotProd = DotProd(normal, ray->GetDir());
    assert(dotProd <=  1.00001);
    assert(dotProd >= -1.00001);
  }*/

  if(!normalValid) {
    FATAL << "Normal is not valid\n";
  }
  
  return normal;
}

const CVector3D&
CHitPointInfo::GetAnisAxis() const
{
#ifdef _DEBUG
  if (point.x == MAXFLOAT) {
    FATAL << " the point was not extrapolated, use hitInfo.Extrap()"
	  << " before CHitPointInfo::GetNormal() is called.\n";
    FATAL_ABORT;
  }
#endif
  float dotProdAn;

  if (!anisAxisValid) {
    assert(ray);
    // !!!!
    // This function must be implemented by the user of this library
    // accessing some geometric object data.
    // !!!!
    // temporary version
    ((CVector3D&)anisAxis).SetValue(1.0, 0.0, 0.0);
	(CVector3D&)anisAxis = CrossProd(anisAxis,normal);
    // !!!!
    // Direction of anisotropy should make angle 90 degrees with normal
    dotProdAn = DotProd(normal, anisAxis);
    assert(dotProdAn <=  0.00001);
    assert(dotProdAn >= -0.00001);
    (bool&)anisAxisValid = true;
  }
  else {
    dotProdAn = DotProd(normal, anisAxis);
    assert(dotProdAn <=  0.00001);
    assert(dotProdAn >= -0.00001);
  }

  return anisAxis;
}

// returns the ideally mirrored vector
const CVector3D&
CHitPointInfo::GetReflectedDir() const
{  
  if ( (Flags() & EE_IdealMirrorValid) == 0) {
    // object and ray must be set, normal must be evaluated !
    assert(ray);
    // constness is logical only for idealMirrorDir
    // We assume that the normal is possibly bumppped
    ray->ReflectRay(*((CVector3D*)(&idealMirrorDir)), normal);
    // and idealMirrorValid flag
    ((CHitPointInfo*)this)->SetFlags(EE_IdealMirrorValid);
  }
  return idealMirrorDir;
}

void
CHitPointInfo::GetDifferentials(CVector3D &u, CVector3D &v) const
{
  if (!AreDifferentialsValid()) {
    // compute the differentials

    // !!!!
    // This function must be implemented by the user of this library
    // accessing some geometric object data.
    // !!!!
    // temporary version
    (CVector3D&)diffU = CVector3D(1, 0, 0);
    (CVector3D&)diffV = CVector3D(0, 1, 0);
    // !!!!
    
    ((CHitPointInfo*)this)->SetFlags(EE_diffsValid);
  }

  if (AreDifferentialsValid()) {
    u = diffU;
    v = diffV;
  }
}

ostream&
operator<<(ostream &s, const CHitPointInfo &h)
{
  // Something can be put here

  return s;
}


bool
CHitPointInfo::ComputeMapping(CMapPointInfo &mapinfo,
			      CMappingBase::EParamType whichParam) const
{
  // !!!!
  // This function must be implemented by the user of this library
  // accessing some geometric object data.
  // !!!!
  // temporary version for Fresnel textures
  if((whichParam == CMappingBase::EE_KFM)||(whichParam == CMappingBase::EE_KFR))
  {
    float cosi = this->GetDotProd();
    float sqr = 1.0 - cosi*cosi;
    (sqr < 0) ? sqr = 0 : sqr = sqr;
    float sini = sqrt(sqr);
    mapinfo.dimType = CMappingBase::EE_3D;
    mapinfo.paramType = whichParam;
    mapinfo.coord.x = cosi;
    mapinfo.coord.y = sini;
    mapinfo.coord.z = sini; // to be set in GetTextureValue
    
    return true;
  }

  if((whichParam == CMappingBase::EE_KFI)||
     (whichParam == CMappingBase::EE_KFE))
  {
    mapinfo.dimType = CMappingBase::EE_2D;
    mapinfo.paramType = whichParam;
    mapinfo.coord.x = 0.5;
    mapinfo.coord.y = 0.5;
    return true;
  }


  mapinfo.dimType = CMappingBase::EE_2D;
  mapinfo.paramType = whichParam;
  mapinfo.coord.x = 0.5;
  mapinfo.coord.y = 0.5;
  return false;
  // !!!!
}


bool
CHitPointInfo::ComputeArbitraryMapping(CMapPointInfo &mapinfo,
			      CMappingBase::EParamType whichParam,
				  const float cosInc) const
{
  // !!!!
  // This function must be implemented by the user of this library
  // accessing some geometric object data.
  // !!!!
  // temporary version for Fresnel textures
  if((whichParam == CMappingBase::EE_KFM)||(whichParam == CMappingBase::EE_KFR))
  {
    float sqr = 1.0 - cosInc*cosInc;
    (sqr < 0) ? sqr = 0 : sqr = sqr;
    float sini = sqrt(sqr);
    mapinfo.dimType = CMappingBase::EE_3D;
    mapinfo.paramType = whichParam;
    mapinfo.coord.x = cosInc;
    mapinfo.coord.y = sini;
    mapinfo.coord.z = sini; // to be set in GetTextureValue
    
    return true;
  }

  if((whichParam == CMappingBase::EE_KFI)||
     (whichParam == CMappingBase::EE_KFE))
  {
    mapinfo.dimType = CMappingBase::EE_2D;
    mapinfo.paramType = whichParam;
    mapinfo.coord.x = 0.5;
    mapinfo.coord.y = 0.5;
    return true;
  }


  mapinfo.dimType = CMappingBase::EE_2D;
  mapinfo.paramType = whichParam;
  mapinfo.coord.x = 0.5;
  mapinfo.coord.y = 0.5;
  return false;
  // !!!!
}

// Get the color the texture point according to the mapping information 
// For information about the mapped parameters see file mappibas.h (KP)
//=============================================================================
void
CHitPointInfo::GetTextureValue(CValues &values, CTexture &texture,
			       const CMappingBase::EParamType whichParam) const
{
  static CMapPointInfo mapinfo1, mapinfo2;
  static CMapPointInfo mapinfo_ar[2];
  int sizeMap = 2;

  // Compute mapping for the specified texture type
  //----------------------------------------------------------------------
  // Note:
  //  1. Fresnel texture is incident/exitant direction dependent => here
  //     should be several types of mapping: for normal incidence, and for
  //     all the rest types of incidence
  //  2. Note that here it can be implemented some caching for the same
  //     'whichParam'
  if (ComputeMapping(mapinfo1, whichParam) &&
             !((whichParam == CMappingBase::EE_KFM) ||
               (whichParam == CMappingBase::EE_KFR) ||
	       (whichParam == CMappingBase::EE_KFI) ||
	       (whichParam == CMappingBase::EE_KFE)))
  {
    return;
  } // mapping failed;

  // For Fresnel texture compute additional mapping for normal incidence
  if((whichParam == CMappingBase::EE_KFM) ||
     (whichParam == CMappingBase::EE_KFR))
  {
    ComputeMapping(mapinfo2, CMappingBase::EE_KFI);
  }

  // Compute RGB value for the texture according to computed mapping
  //-----------------------------------------------------------------------
  if((whichParam == CMappingBase::EE_KFM) ||
     (whichParam == CMappingBase::EE_KFR))
  {
    // Compute the RGB value for Fresnel texture
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    CValues iora(0,0,0), extinctiona(0,0,0);
    float ior = 0, extinction = 0;
    bool approx = false;
    bool use_fresnel = texture.GetRefractionParams(iora, extinctiona,
                                                   approx, mapinfo2);
    ior        = iora.Get(0);
    extinction = extinctiona.Get(0);

    if(use_fresnel)
    {
      // Note, that ior - relative index of refraction and it should be
      // carefully set by the user. (For metals parameter sine of refracted
      // angle does not make sense). For dielectric it should be ior2/ior1
      float sinr = 0;
      if(ior >= 1e-06)
        sinr = mapinfo1.coord.y / ior;
      mapinfo1.coord.z = sinr;
    }

    mapinfo_ar[0] = mapinfo1;
    mapinfo_ar[1] = mapinfo2;
    texture.GetValue(values, mapinfo_ar, sizeMap);
    return;
  }
  else
  {
    // Compute the RGB value for Fresnel texture at normal incidence
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Index of refraction and coefficient of extinction in common case are 
    // also RGB textures and its values can vary for different parts of the
    // surface. In this way there should be a possibility to get RGB values 
    // of these coefficients according to specified mapping information
    if((whichParam == CMappingBase::EE_KFI) ||
       (whichParam == CMappingBase::EE_KFE))
    {
      CValues iora(0,0,0), extinctiona(0,0,0);
      bool approx = false;
      bool use_fresnel = texture.GetRefractionParams(iora, extinctiona,
                                                     approx, mapinfo1);
      if(whichParam == CMappingBase::EE_KFI)
        values = iora;
      else
        values = extinctiona;
    }
    else
    {
      // return texture value for a other types of mapping
      texture.GetValue(values, mapinfo1);
    }
    return;
  }
}

// Get the color the texture point according to the mapping information 
// For information about the mapped parameters see file mappibas.h (KP)
//=============================================================================
void
CHitPointInfo::GetTextureValue(CValues &values, CTexture &texture,
			       const CMappingBase::EParamType whichParam,
				   const float cosInc) const
{
  static CMapPointInfo mapinfo1, mapinfo2;
  static CMapPointInfo mapinfo_ar[2];
  int sizeMap = 2;

  // Compute mapping for the specified texture type
  //----------------------------------------------------------------------
  // Note:
  //  1. Fresnel texture is incident/exitant direction dependent => here
  //     should be several types of mapping: for normal incidence, and for
  //     all the rest types of incidence
  //  2. Note that here it can be implemented some caching for the same
  //     'whichParam'
  if (ComputeArbitraryMapping(mapinfo1, whichParam, cosInc) &&
             !((whichParam == CMappingBase::EE_KFM) ||
               (whichParam == CMappingBase::EE_KFR) ||
	       (whichParam == CMappingBase::EE_KFI) ||
	       (whichParam == CMappingBase::EE_KFE)))
  {
    return;
  } // mapping failed;

  // For Fresnel texture compute additional mapping for normal incidence
  if((whichParam == CMappingBase::EE_KFM) ||
     (whichParam == CMappingBase::EE_KFR))
  {
    ComputeArbitraryMapping(mapinfo2, CMappingBase::EE_KFI, cosInc);
  }

  // Compute RGB value for the texture according to computed mapping
  //-----------------------------------------------------------------------
  if((whichParam == CMappingBase::EE_KFM) ||
     (whichParam == CMappingBase::EE_KFR))
  {
    // Compute the RGB value for Fresnel texture
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    CValues iora(0,0,0), extinctiona(0,0,0);
    float ior = 0, extinction = 0;
    bool approx = false;
    bool use_fresnel = texture.GetRefractionParams(iora, extinctiona,
                                                   approx, mapinfo2);
    ior        = iora.Get(0);
    extinction = extinctiona.Get(0);

    if(use_fresnel)
    {
      // Note, that ior - relative index of refraction and it should be
      // carefully set by the user. (For metals parameter sine of refracted
      // angle does not make sense). For dielectric it should be ior2/ior1
      float sinr = 0;
      if(ior >= 1e-06)
        sinr = mapinfo1.coord.y / ior;
      mapinfo1.coord.z = sinr;
    }

    mapinfo_ar[0] = mapinfo1;
    mapinfo_ar[1] = mapinfo2;
    texture.GetValue(values, mapinfo_ar, sizeMap);
    return;
  }
  else
  {
    // Compute the RGB value for Fresnel texture at normal incidence
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Index of refraction and coefficient of extinction in common case are 
    // also RGB textures and its values can vary for different parts of the
    // surface. In this way there should be a possibility to get RGB values 
    // of these coefficients according to specified mapping information
    if((whichParam == CMappingBase::EE_KFI) ||
       (whichParam == CMappingBase::EE_KFE))
    {
      CValues iora(0,0,0), extinctiona(0,0,0);
      bool approx = false;
      bool use_fresnel = texture.GetRefractionParams(iora, extinctiona,
                                                     approx, mapinfo1);
      if(whichParam == CMappingBase::EE_KFI)
        values = iora;
      else
        values = extinctiona;
    }
    else
    {
      // return texture value for a other types of mapping
      texture.GetValue(values, mapinfo1);
    }
    return;
  }
}


__END_BRDFLIB_SOURCE
