// ===================================================================
// $Id: phong.h,v 1.6 2003/10/30 16:36:35 havran Exp $
//
// phong.h
//     Header file for CSurfacePhong class.
//
// Class: CSurfacePhongBase, 
//		  CSurfacePhong, 
//        CSurfacePhongPhysical, 
//        CSurfacePhongGrazingMirror,
//        CSurfacePhongPhysicalDR (DR means deterministic reflection)
//
// 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, 1999; Jaroslav Krivanek (Nov 2000)
//
// Given to BRDFLIB project for the Polina Kondratieva's
// HIWI JOB assignment in November 2003.
//
// Edited by Jiri Nekola within diploma thesis, September 2011
// ===================================================================

#ifndef __PHONG_H__
#define __PHONG_H__

// BRDFLIB library
#include "configh.h"
#include "surface.h"
#include "functions.h"


__BEGIN_BRDFLIB_HEADER


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

// Refernce:
//  1. Glassner, "Principles of Digital Image Synthesis", Vol. 2
//  2. Dutre, P. "Global Illumination Compendium", 2001
//  3. Neumann, A. Ph.D. "Construction of Bidirectional Reflection Distribution
//     Functions", 2001
//  4. Blinn, J.F. Models of Light Reflection fo Computer Synthetized
//     Pictures", SIGGRAPH`77, In Computer Graphics 11(2), pp. 192-198
//  5. Lewis, R.B. "Making Shaders More Physically Plausible", Eurographics'93
//  6. Lafortune, E. "Non-linear Approximation of Reflectance Function",
//     SIGGRAPH, 1997
//  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 A., "Compact Metallic Reflectance Models", EUROGRAPHICS, 1999
//  14. Phong B. T., "Illumination for computer generated images",
//		Communications of the ACM, 18, pp. 311-317, 1975

// ----------------------------------------------------------------------------
// This is the base for phong surfaces.
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// This is the Phong surface abstract class.
class CSurfacePhongBase:
  public CSurface
{
public:
  // constructor
  CSurfacePhongBase():
    CSurface(EE_NONE),
    _diffuse(0),
    _specular(0),
    _specnum(0),
    _refract(0),
    _index(0),
    _extinction(0)
  {
     _maxPhong = false;
     _normalsBlinn = false;
     _retroReflective = false;
  }

  virtual ~CSurfacePhongBase()
  {
	if (_diffuse) delete _diffuse;
	if (_specular) delete _specular;
	if (_specnum) delete _specnum;
	if (_extinction) delete _extinction;
	if (_index) delete _index;
	if (_refract) delete _refract;
  }

  // this is deep copy
  virtual CSurface *Dup() const;
  virtual CSurfacePhongBase* CreateNewObject() const = 0;

  // Set diffuse color of the object
  inline void SetDiffuse(CTexture *texture)
  {
    AssignPointer(_diffuse,texture);
    if (texture)
      _setSupportedType(EE_REFLECTED_DIFFUSE);
    else
      _unsetSupportedType(EE_REFLECTED_DIFFUSE);
  }

  // Get diffuse color of the object
  inline CTexture* GetDiffuse() { return _diffuse;}

  // Set specular color of the object
  inline void SetSpecular(CTexture *ks)
  {
	 AssignPointer(_specular,ks);
     if (ks)
       _setSupportedType(_phongReflectionType());
     else
       _unsetSupportedType(_phongReflectionType());
  }
  
  // set shininess of the object
  inline void SetShininess(CTexture *shine) 
  {
    AssignPointer(_specnum,shine);
  }

  // Get specular color of the object
  inline CTexture* GetKs() { return _specular; }

  // Get shininess parameter for the specular part of BRDF
  inline CTexture* GetShininess() { return _specnum; }

  // Set parameters of refraction (fraction of rays to be refracted
  inline void SetRefract(CTexture *kr)
  {
    AssignPointer(_refract,kr);
    if (kr)
      _setSupportedType(EE_REFRACTED_PERFECT);
    else
      _unsetSupportedType(EE_REFRACTED_PERFECT);
  }

  // set index of refraction of the object
  inline void SetIOR(CTexture *ior)
  {
    AssignPointer(_index,ior);
  }
  // Set coeffitient of extinction (complex index of refraction for metals)
  inline void SetExtinction(CTexture *extinction)
  {
	AssignPointer(_extinction,extinction);
    if (extinction)
      _setSupportedType(EE_REFLECTED_PERFECT);
    else
      _unsetSupportedType(EE_REFLECTED_PERFECT);
  }

  // Get a fraction of rays to be refracted
  inline CTexture* GetRefract() { return _refract; }
  // Get index of refraction
  inline CTexture* GetIor()     { return _index; }
  // Get index of extinction
  inline CTexture* GetExtinction()  { return _extinction; }

  // Switch to MaxPhong model (albedo pumping technique for BRDF [3], p.34)
  virtual void SetMaxPhong(bool &maxPhong) { _maxPhong = maxPhong; }

  // Switch to retro reflective brdf as proposed in [13]
  virtual void SetRetroReflective(bool reflective) { _retroReflective = reflective;}

  // Switch to Blinn-Phong BRDF model
  virtual void SetBlinnNormals(bool &blinnNormals)
  { _normalsBlinn = blinnNormals; }

  virtual EType GetType() const { return EE_Phong;}

  // For description of these functions see surface.h
  //----------------------------------------------------------------------
  virtual bool
  GetAlbedoMax(CValues &albedo,
	       const CHitPointInfo &hitInfo,
	       EDistributionType   distType=EE_ALL) const;

  virtual bool
  GetAlbedo(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 bool GetTransparency(CValues &transparency,
			       const CHitPointInfo &hitinfo) const;

  virtual bool GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) const;

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

  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;
  //----------------------------------------------------------------------
private:
  CSurfacePhongBase& operator=(const CSurfacePhongBase&);
  CSurfacePhongBase(const CSurfacePhongBase&);
protected:
  CTexture *_diffuse;	      // Diffuse color of the object

  CTexture *_specular;	      // SIndex of specular reflection
  CTexture *_specnum;	      // Specularity factor

  CTexture *_refract;	      // How much light is refracted

  // For metals the index of refraction is complex [1]:
  //       N = n + ik,
  // where n - is the ratio of wavelenths in vacuum and study medium
  //       k - is the index of extinction
  //---------------------------------------------------------------------------
  CTexture *_index;	      // Index of refraction for object for dielectrics
                              // Ration of wavelengths in vacuum and medium
			      // (real part of index of refraction for metals)
  CTexture *_extinction;      // Index of extinction for metal object
                              // (imaginary part of index of refraction)

  bool _maxPhong;             // Use MaxPhong of Phong reciprocial
  bool _normalsBlinn;         // Use Blinn or Classical shading normals
  bool _retroReflective;      // Use brdf for retro-reflective materials as proposed in [13]

  inline void ScaleWithTransmission(CValues &res,
				    const CHitPointInfo &hitInfo) const;
  // This must be implemented by every phong surface and must return
  // either EE_REFLECTED_PERFECT or EE_REFLECTED_GLOSSY according
  // to the way in which the reflected rays are actually spawned
  virtual EDistributionType _phongReflectionType() const = 0;

protected:
  // the auxiliary storage of random variable for stochastic sampling
  typedef vector<double> rnd_array;
  static double aux_rnd;
  static rnd_array diffuse_rnd;
  static rnd_array specular_rnd;
  static rnd_array refraction_rnd;

  void SetAuxRnd(double rnd_num, float min, float max) const {
    assert( (rnd_num >= min) && (rnd_num <= max));
    aux_rnd = (rnd_num - min) / (max - min);
  }
  void SetAuxRnd(double rnd_num) const { aux_rnd = rnd_num; }
};

// ----------------------------------------------------------------------------
// This class represents the classical Phong's shading model [14]. 
// ----------------------------------------------------------------------------
class CSurfacePhong:
  public CSurfacePhongBase
{
public:
  CSurfacePhong():
    CSurfacePhongBase()
  {}

  virtual CSurfacePhongBase* CreateNewObject() const
  { return new CSurfacePhong; }

  virtual int
  SpawnDeterministicRays(CContributionArray  &carray,
		         const CHitPointInfo &hitInfo,
       		         EDistributionType   distType = EE_ALL) const
  {
     return 0;
  }

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

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

  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:
  virtual EDistributionType _phongReflectionType() const
  { return EE_REFLECTED_GLOSSY; }

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

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

// ----------------------------------------------------------------------------
// This class represents the physicaly correct modification of Phong's
// shading model [5]. This is correct in the sense: 0.0 <= albedo <= 1.0
// Reflection rays of this surface are randomized.
// ----------------------------------------------------------------------------
class CSurfacePhongPhysical:
  public CSurfacePhongBase
{
public:
  CSurfacePhongPhysical():
    CSurfacePhongBase()
  {}

  virtual CSurfacePhongBase* CreateNewObject() const
  { return new CSurfacePhongPhysical; }

  virtual int
  SpawnDeterministicRays(CContributionArray  &carray,
		         const CHitPointInfo &hitInfo,
       		         EDistributionType   distType = EE_ALL) const
  {
     return 0;
  }

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

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

  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:
  virtual EDistributionType _phongReflectionType() const
  { return EE_REFLECTED_GLOSSY; }

  // used by SpawnStochasticRays
  int SpawnIdealRefractedRay(const CValues       &kr_local,
			     CContributionArray  &carray,
			     const CHitPointInfo &hitInfo) const;

  // there are two types of SpawnStochastic____Rays methods
  //
  // first, that generates new rays using random numbers from 
  // standard static random array (not optimized for multi-threading)
  // - only one thread can access these arrays = these methods at a time
  //
  // second, that uses local random arrays -> optimized for 
  // multithreading, because each thread has its own random arrays so
  // it can be accessed paralelly
  //
  // first set of these methods are here only because they are used
  // in another classes in standard way (using static random arrays)
  // - only Phong physical uses this optimization, so there is no need
  // to reimplement all the classes that uses these spawning methods as well

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

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

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

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

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


// ----------------------------------------------------------------------------
// This class represents combination of classical Phong model and function
// which adds specularity at grazing angles (see [6] p. 5)
// ----------------------------------------------------------------------------
class CSurfacePhongGrazingMirror:
  public CSurfacePhongPhysical
{
public:
  CSurfacePhongGrazingMirror():
    CSurfacePhongPhysical()
  {}

  virtual ~CSurfacePhongGrazingMirror() {}

  virtual CSurfacePhongBase* CreateNewObject() const
  { return new CSurfacePhongGrazingMirror; }

  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;


protected:
  virtual EDistributionType _phongReflectionType() const
  { return EE_REFLECTED_GLOSSY; }


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

  virtual const char* GetHelp() const;

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


// ----------------------------------------------------------------------------
// This is the same as CSurfacePhongPhysical, but reflection is deterministic,
// to the direction of the perfectly reflected ray.
// ----------------------------------------------------------------------------
class CSurfacePhongPhysicalDR:
  public CSurfacePhongPhysical
{
public:
  CSurfacePhongPhysicalDR():
    CSurfacePhongPhysical()
  { }

  virtual ~CSurfacePhongPhysicalDR() {}

  virtual CSurfacePhongBase* CreateNewObject() const
  { return new CSurfacePhongPhysicalDR; }

  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;

protected:
  virtual EDistributionType _phongReflectionType() const
  { return EE_REFLECTED_PERFECT; }

  int  SpawnIdealReflectedRay(const CValues       &ks_local,
			      CContributionArray  &carray,
			      const CHitPointInfo &hitInfo)  const;

  virtual int SpawnStochasticDiffuseRays(const CValues       &kd_local,
				 CContributionArray  &carray,
				 const CHitPointInfo &hitInfo,
				 CSamplingInfo        &samplingInfo,
				 int                 maxRays) const;
  virtual const char* GetHelp() const;
private:
  CSurfacePhongPhysicalDR& operator=(const CSurfacePhongPhysicalDR&);
  CSurfacePhongPhysicalDR(const CSurfacePhongPhysicalDR&);
};



__END_BRDFLIB_HEADER

#endif // __PHONG_H__
