// ===================================================================
// $Id:
//
// lafortune.h
//     Implementation of Lafortune model
//
// Class: CSurfaceLafortune.
//
// Initial coding by Polina Kondratieva (PK), January 2004.
//
// Edited by Jiri Nekola within diploma thesis, September 2011
// ===================================================================

#ifndef __LAFORTUNE_H__
#define __LAFORTUNE_H__

// BRDFLIB library
#include "configh.h"
#include "phong.h"


__BEGIN_BRDFLIB_HEADER

// Forward-declare classes
class CHitPointInfo;
class CMatrix4;
class CRay;
class CTexture;

// Refernce:
//  1. Lafortune, E. "Non-linear Approximation of Reflectance Function",
//     SIGGRAPH, 1997
//  2. Glassner, "Principles of Digital Image Synthesis", Vol. 2
//  3. Dutre, P. "Global Illumination Compendium", 2001
//  4. Neumann, A. Ph.D. "Construction of Bidirectional Reflection Distribution
//     Functions", 2001
//  5. Blinn, J.F. Models of Light Reflection fo Computer Synthetized
//     Pictures", SIGGRAPH`77, In Computer Graphics 11(2), pp. 192-198
//  6. Lewis, R.B. "Making Shaders More Physically Plausible", Eurographics'93
//  7. Veach, E. "Robust Monte Carlo Methods for Light Transport Simulation",
//     Ph.D., Stanford University, 1997.
//  8. Suykens, F. "A Mathematical Model for Light Transport", Ph.D.
//  9. Shirley, P. Wang, C. "Luminaire Sampling in Distribution Ray Tracing",
//     SIGGRAPH, Course Notes 44, 1993.
//  10. Arvo, J. "Applications of Irradiance Tensors to the Simulation of
//     Non-Lambertian Phenomena", SIGGRAPH, 1995
//  11. Shirley, "Realistic Ray Tracing", 2003
//  12. Szirmay-Kalos, L. "Monte-Carlo Methods in Global Illumination", 2000
//  13. Neumann, "Compact Metallic Reflectance Models", EUROGRAPHICS, 1999


// ------------------------------------------------------------------------
// Lafortune model. A generalization of the cosine lobe model that is
// based on the Phong shading model. It is intended to approximate the
// directional-diffuse component and possibly a non-Lambertian diffuse
// component of a reflectance function [1].
//
// For this model incident and exitant directions are defined in the local
// coordinate system at the surface. The coordinate system is aligned to
// the normal and to the principal directions of anisotropy, if any.
// ------------------------------------------------------------------------
/*
  Explanation how to find local Vector coordinates if coordinates of
  the basis vectors and Vector coordinates in the World coordinate
  system are given.

  1. Transformation as a change of basis
  --------------------------------------
  At first let us show how the trasformation between the two coordinate
  systems having the same origin but different orientations can be found.

  Given: global coordinate system CS1, new coordinate system CS2 (basis
         vectors(_m, _n, _o) of CS2 are described in CS1), coordinates of
	 vector V(x,y,z) in CS1.
  Find:  coordinates of vector V in CS1 (a,b,c).

  Solution:
    P(x,y,z) = a*_m + b*_n + c*_o
             = a*[1,0,0]^T    + b*[0,1,0]^T    + c*[0,0,1]^T      in CS2
	     = a*[mx,my,mz]^T + b*[nx,ny,nz]^T + c*[ox,oy,oz]^T   in CS1

    | x |     | mx  nx  ox  0 |    | a |
    | y |  =  | my  ny  oy  0 | *  | b |
    | z |     | mz  nz  oz  0 |    | c |
    | 1 |     |  0   0   0  1 |    | 1 |

  2. 'Inverse' transformation - our goal
  --------------------------------------
  Basis is orthonormal (Dot(_m,_n)=0, Dot(_m,_o)=0, Dot(_n,_o)=0), =>

       MT                     M
  | mx  my  mz  0 |   | mx  nx  ox  0 |     | 1  0  0  0 |
  | nx  ny  ny  0 | * | my  ny  oy  0 |  =  | 0  1  0  0 |         =>
  | ox  oy  oz  0 |   | mz  nz  oz  0 |     | 0  0  1  0 |
  |  0   0   0  1 |   |  0   0   0  1 |     | 0  0  0  1 |

  If we multiply both parts of the equation by MT, we get:
  | mx  my  mz  0 |   | x |     | a |
  | nx  ny  ny  0 | * | y |  =  | b |   (MT * V = _V)              =>
  | ox  oy  oz  0 |   | z |     | c |
  |  0   0   0  1 |   | 1 |     | 1 |

  _Vx = a = Dot(_m, V)
  _Vy = b = Dot(_n, V)
  _Vz = c = Dot(_o, V)
*/

class CSurfaceLafortune:
  public CSurfacePhongPhysical
{
public:
  CSurfaceLafortune():
    CSurfacePhongPhysical()
	{
		_currentLobesCount = 1; // one lobe as default
	}

  virtual ~CSurfaceLafortune() 
  {
	  ClearCxCyCz();
	  ClearSpecnums();
  }

  virtual CSurface *Dup() const;

  virtual CSurfaceLafortune* CreateNewObject() const
  { return new CSurfaceLafortune; }

  // Should be of EE_Weigth1 type
  inline vector<CTexture*> GetCx() { return _cx;}
  inline vector<CTexture*> GetCy() { return _cy;}
  inline vector<CTexture*> GetCz() { return _cz;}
  inline vector<CTexture*> GetSpecnums() { return _specnums;}


  inline CTexture* GetKs() { return _specular; }

  inline void SetSpecular(CTexture *ks)
  {
	AssignPointer(_specular,ks);
     if (ks){
       EDistributionType e = _phongReflectionType();
       _setSupportedType(e);
     }
     else
       _unsetSupportedType(_phongReflectionType());
  }

  inline void SetCxCyCz(vector<CTexture*> cx, vector<CTexture*> cy,
                        vector<CTexture*> cz)
  {
    assert(cx.size() > 0 && cy.size() > 0 && cz.size() > 0);
    assert(cx.size() == cy.size() && cx.size() == cz.size());

	ClearCxCyCz();
    _cx = cx;        _cy = cy;        _cz = cz;
	_currentLobesCount = cx.size();
  }

  inline void SetCx(vector<CTexture*> cx)
  {
    if(((_cy.size() > 0) && (cx.size() != _cy.size())) ||
       ((_cz.size() > 0) && (cx.size() != _cz.size())))
    {
      WARNING_INFO("The number of coefficients Cx, Cy and Cz should be the same");
      return;
    }
    ClearCx();
    _cx = cx;
	_currentLobesCount = cx.size();
  }

  inline void SetCy(vector<CTexture*> cy)
  {
    if(((_cx.size() > 0) && (cy.size() != _cx.size())) ||
       ((_cz.size() > 0) && (cy.size() != _cz.size())))
    {
      WARNING_INFO("The number of coefficients Cx, Cy and Cz should be the same");
      return;
    }
    ClearCy();
    _cy = cy;
	_currentLobesCount = cy.size();
  }
  inline void SetCz(vector<CTexture*> cz)
  {
    if(((_cy.size() > 0) && (cz.size() != _cy.size())) ||
       ((_cx.size() > 0) && (cz.size() != _cx.size())))
    {
       WARNING_INFO("The number of coefficients Cx, Cy and Cz should be the same");
      return;
    }
    ClearCz();
    _cz = cz;
	_currentLobesCount = cz.size();
  }
  inline void SetSpecnums(vector<CTexture*> specnums)
  {
    if(((_cx.size() > 0) && (specnums.size() != _cx.size())) ||
       ((_cy.size() > 0) && (specnums.size() != _cy.size())) ||
       ((_cz.size() > 0) && (specnums.size() != _cz.size())))
    {
      WARNING_INFO("The number of coefficients Cx, Cy, Cz and shininess should be the same");
      return;
    }

    ClearSpecnums();
    _specnums = specnums;
	_currentLobesCount = specnums.size();
  }

  virtual bool
  EvaluateBSDF(CValues             &brdf,
	       const CHitPointInfo &hitInfo,
	       const CVector3D     &out,
	       EDistributionType   distType=EE_ALL) const;

  virtual int
  SpawnStochasticRays(CContributionArray  &carray,
		      const CHitPointInfo &hitInfo,
		      CSamplingInfo       &samplingInfo,
      		      EDistributionType   distType = EE_ALL) const;

  virtual bool
  GetAlbedo(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType=EE_ALL) const;

  virtual bool
  GetAlbedoMax(CValues &albedo,
	       const CHitPointInfo &hitInfo,
	       EDistributionType   distType=EE_ALL) const;

  virtual float GetAverageDiffuseness(void) const;

  virtual float GetDiffuseness(const CHitPointInfo &hitinfo,
			       EDistributionType distType=EE_ALL) const;

  virtual void Describe(ostream &app, int ind) const;

  virtual void SetMaxLafortune(bool max) { _maxLafortune = max;}

  virtual void AddLobe(CTexture* cx, CTexture* cy, CTexture* cz, CTexture* specnum);

  virtual const char* GetParameterTitle(int parameterIndex) const;

  virtual const char* GetParameterDescription(int parameterIndex) const;

  virtual EParameterUIType GetParameterSyntax(int parameterIndex) const;

  virtual void SetParameter(int parameterIndex, 
							const CValues& value);

  virtual CValues GetParameter(int parameterIndex) const;

  virtual CValues GetParameterConstraints(int parameterIndex) const;

  virtual int GetNumParameters() const;

  virtual const char* GetHelp() const;

protected:
  /// vectors deleters
  void ClearCx()
  {
	  int size = _cx.size();
	  if (size)
	  {
		  for (int i = 0; i < size; i++)
		  {
			  delete _cx[i];
		  }
		  _cx.clear();
	  }
  }
  void ClearCy()
  {
	  int size = _cy.size();
	  if (size)
	  {
		  for (int i = 0; i < size; i++)
		  {
			  delete _cy[i];
		  }
		  _cy.clear();
	  }
  }
  void ClearCz()
  {
	  int size = _cz.size();
	  if (size)
	  {
		  for (int i = 0; i < size; i++)
		  {
			  delete _cz[i];
		  }
		  _cz.clear();
	  }
  }
  void ClearSpecnums()
  {
	  int size = _specnums.size();
	  if (size)
	  {
		  for (int i = 0; i < size; i++)
		  {
			  delete _specnums[i];
		  }
		  _specnums.clear();
	  }
  }
  void ClearCxCyCz()
  {
	ClearCx();
	ClearCy();
	ClearCz();
  }

protected:
  vector<CTexture *> _cx;       // array of coeffitients for x local direction
  vector<CTexture *> _cy;       // array of coeffitients for y local direction
  vector<CTexture *> _cz;       // array of coeffitients for z local direction
  vector<CTexture *> _specnums; // shininess

  bool _maxLafortune; //use correction factor as in maxPhong, proposed in [13]

  int _currentLobesCount; // count of lobes (which equals to size of _cx vector (= size of _cy vector = size of _cz vector = size of _specnum vector))

  virtual EDistributionType _phongReflectionType() const
  { return EE_REFLECTED_GLOSSY; }

  int SpawnStochasticSpecularRays(const CValues       &ks_local,
				  CContributionArray  &carray,
				  const CHitPointInfo &hitInfo,
				  CSamplingInfo        &samplingInfo,
				  int                 maxRays) const;

  int SpawnStochasticDiffuseRays(const CValues       &kd_local,
				 CContributionArray  &carray,
				 const CHitPointInfo &hitInfo,
				 CSamplingInfo        &samplingInfo,
				 int                 maxRays) const;
  


private:
  CSurfaceLafortune& operator=(const CSurfaceLafortune&);
  CSurfaceLafortune(const CSurfaceLafortune&);
};


__END_BRDFLIB_HEADER

#endif // __LAFORTUNE_H__
