// ===================================================================
// $Id: surface.h,v 1.6 2003/10/30 16:36:35 havran Exp $
//
// surface.h
//     Header file for CSurface classes.
//
// Class: CSurface, CSurfaceList.
//
// 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 for the use to BRDFLIB project for the Polina Kondratieva's
// HIWI JOB assignment in November 2003.
//
// Edited by Jiri Nekola within diploma thesis, December 2010
// ===================================================================

// Comments:
// The class CSurface in principle corresponds to a BRDF model.
//
// The design here was highly inspired by:
// a) Szirmay-Kalos Laslo: Monte-Carlo Methods in Global Illumination,
//    WS 1999/2000, script in Vienna University of Technology
// b) Design of PhBRML by Philippe Bekaert
// c) Vision Architecture by Phillip Slusallek
// d) Spectrum Architecture by Andrew Glassner
// e) talking with Jan Prikryl and Jiri Bittner

#ifndef __SURFACE_H__
#define __SURFACE_H__

// BRDFLIB library
#include "configh.h"
#include "color.h"
#include "contrib.h"
#include "samplinf.h"
#include "strings.h"

// standard headers
#include <vector>


__BEGIN_BRDFLIB_HEADER

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

// ----------------------------------------------------------------
// CSurfaceList
//  List of colors; derives from CSimpleList and adds a function
//  to search for a given color and return nonzero if there.
// ----------------------------------------------------------------
class CSurfaceList:
  public vector<CSurface *>
{
public:
  // return true if 'probe' is in the list, otherwise false.  
  bool SurfaceThere(CSurface *probe);
};

enum EDistributionType
{
  // no rays are spawned
  EE_NONE = 0x0,

  // the type of interaction
  EE_REFLECTED_DIFFUSE   = 0x1,
  
  EE_REFLECTED_IMPERFECT  = 0x2,
  EE_REFLECTED_GLOSSY     = 0x2, // synonym
  EE_REFLECTED_SPECULAR   = 0x2, // and yet another synonym
  
  EE_REFLECTED_PERFECT    = 0x4,
  EE_REFLECTED_MIRROR     = 0x4, // synonym
  
  EE_REFLECTED_DIRECTIONAL =
  EE_REFLECTED_GLOSSY | EE_REFLECTED_PERFECT,

  EE_REFLECTED =
  EE_REFLECTED_DIFFUSE | EE_REFLECTED_GLOSSY | EE_REFLECTED_PERFECT,
  
  EE_REFRACTED_DIFFUSE   = 0x10,
  
  EE_REFRACTED_IMPERFECT  = 0x20,
  EE_REFRACTED_GLOSSY     = 0x20, // synonym
  EE_REFRACTED_SPECULAR   = 0x20, // and yet another synonym
  
  EE_REFRACTED_PERFECT    = 0x40,
  
  EE_REFRACTED_DIRECTIONAL =
  EE_REFRACTED_GLOSSY | EE_REFRACTED_PERFECT,
    
  EE_REFRACTED =
  EE_REFRACTED_DIFFUSE | EE_REFRACTED_GLOSSY | EE_REFRACTED_PERFECT,

  EE_DIRECTIONAL =
  EE_REFLECTED_DIRECTIONAL | EE_REFRACTED_DIRECTIONAL,
  
  EE_DIFFUSE =
  EE_REFLECTED_DIFFUSE | EE_REFRACTED_DIFFUSE,
  // reflected and refracted rays are spawned
  EE_ALL = EE_REFLECTED | EE_REFRACTED
};


// ------------------------------------------------------------------------
// This class describes the most general interface to physical
// description of a surface for any geometric object. This implies
// the propertis of reflecting and refracting the rays
// This is surely abstract class with implicit constructor
// ------------------------------------------------------------------------
class CSurface
{
public:
  // this enum is used to determine which rays can be spawned, when the
  // method SpawnRays is called.  
  enum EType {
    EE_None                = 0x0,
    // Phong description of surface should be used
    EE_Phong               = 0x1,
    // MGF description of surface is used
    EE_MGF                 = 0x2,
    // Bidirectional (reflectance/transmittance) distribution function
    // given by table
    EE_BSDF                = 0x4,
    
    // Ideally diffuse surface (Lambertian)
    EE_BSDFIdealDiffuseRefl    = 0x8,
    // Ideal mirror surface (reflecting at one direction)
    EE_BSDFIdealMirrorRefl     = 0x10,
    // Ideally specular (simulates reciprocal Phong type reflection)
    EE_BSDFSpecularRefl        = 0x20,
    
    // Ideally refracting diffuse surface (Lambertian)
    EE_BSDFDiffuseRefr         = 0x40,
    // Ideal refracting surface (refracting at one direction)
    EE_BSDFIdealRefr           = 0x80,
    // Ideally refracting specular (simulates reciprocal Phong type refraction)
    EE_BSDFSpecularRefr        = 0x100,
    
    // General surface
    // (composed of IdealDiffuse, Mirror, Refractive,and Specular)
    EE_BSDFComposed            = 0x200,

    EE_DBBRDF                  = 0x400,  // BRDF read from the Database

    // BRDF of real metals (actually, it is IdealReflectorClass, but instead
    // of constant coeffitient of reflection is used one computed according
    // to Fresnel  formulae.
    EE_REAL_METAL              = 0x800,

     // BRDF of real dielectrics (actually, it is IdealRefractorClass, but
     // instead of constant coeffitient of reflection is used one computed
     // according to Fresnel  formulae.
    EE_REAL_DIELECTRIC         = 0x1000,

    // Microfacet-based BRDF models. Only reflected rays are shot.
    EE_BSDFMicrofacetRefl      = 0x2000,

	// Multi-layered based BRDF models.
    EE_BSDFLayered     = 0x4000
 };

  // this enum is used to determine type of the parameter, when the
  // method GetParameterSyntax() is called.  
  enum EParameterUIType
  {
	// unknown type of the parameter
	EE_Unknown  = 0x0000,
	// one float - x
	EE_Float	= 0x0001,
	// two floats - x,y
	EE_XY		= 0x0002,
	// weighted float value - used e.g. for standard deviation of the surface slope
	// of multi-slopiness brdfs
	EE_Weighted_Float = 0x0004,
	// RGB color
	EE_ColorRGB = 0x0008,
	// RGB components parameter
	EE_RGB_Components = 0x0010,
	// boolean value
	EE_Boolean  = 0x0020,
	// 3-component values (x,y,z)
	EE_XYZ = 0x0040,
	// 4-component values (x,y,z and weight)
	EE_Weighted_XYZ = 0x0080,
	// 4-component values (x,y,z,w)
	EE_XYZW = 0x0100,
	// Special type of texture consisting of several basic types
	EE_Fresnel = 0x0200,
	// Surface layer
	EE_Layer = 0x400
  };

  // constructor
  CSurface(EDistributionType t): _supportedType(t) {}

  // destructor, explicitely virtual
  virtual ~CSurface();
  
  // the deep copy operator
  virtual CSurface *Dup() const = 0;

  /**
    Spawn a single ray deterministically (classical recursive ray tracing)

    The ray may be refracted or reflected.

    The ray makes more than 1 bounce if it touches ideal refractive or
    reflective surface. Otherwise, recursion stops at the first bounce.
  **/
  virtual int
  SpawnDeterministicRays(CContributionArray  &carray,
		         const CHitPointInfo &hitInfo,
       		         EDistributionType   distType = EE_ALL) const=0;

  /**
     Spawn at most N stochastic ray (distributed raytracing & pathtracing
     interface).

     For each ray to be spawned, it performs Russian Roulette to decide
     whether to spawn the ray or not. If it is not spawned, absorbtion occured.
     
     The spawned ray can be either reflected or refracted.

     The parameters are the same as for  `SpawnDeterministicRays'.
     Moreover, for the spawned ray, the type of interaction is saved in
     contributionArray.
  */
  virtual int
  SpawnStochasticRays(CContributionArray  &carray,
		      const CHitPointInfo &hitInfo,
		      CSamplingInfo       &samplingInfo,
      		      EDistributionType   distType = EE_ALL) const=0;

  /**
     BSDF (bidirectional scattering distribution function) interface.

     Given a ray, intersection point, and the output vector,
     the ratio of outcoming radiance [W/m^2/sr] to incoming irradiance [W/m^2]
     is set to `res'.
     If the value of BSDF at given point is zero, bsdf is left untouched and
     false is returned.

     @param bsdf(out)    The spectral BSDF.
     @param hitInfo(in)  Incoming light direction for light tracing,
                         viewing direction for (visibility) raytracing.
     @param out(in)      Outgoing direction. It is:
                          - vector from point of intersection to light source
			    for shadow rays
			  - outgoing direction of photon for light tracing
			  - incoming direction of light (gathered light)
			    for (visibility) raytracing
     
     @return true, if the ratio is non-zero, else false.
  */
  virtual bool
  EvaluateBSDF(CValues             &bsdf,
	       const CHitPointInfo &hitInfo,
	       const CVector3D     &out,
	       EDistributionType    distType = EE_ALL) const = 0;

  /** Returns 'true' if the cosine term inside BRDF formula is not evaluated
      - then the value of EvaluateBSDF corresponds to transfer probability
      density and it is used for physically non-based computation to save
      the computation time like the ray tracing. It returns 'false' for the
      default behaviour of EvaluateBSDF: the result of EvaluateBSDF is then
      really BSDF = weight / cos(angle).
  */
  virtual bool CosTermOutOfBSDF() const { return false;}
  
  /**
     Returns albedo at a given point and incoming direction.

     The returned value should be between 0.0 and 1.0 and says how much
     of the incoming light from given direction is propagated (ref(le|ra)cted).
     That is, the probability of absorbtion is (1.0 - albedo).

     @param hitInfo(in) the point and incoming direction

     @param albedo the albedo in each spectral band

     @return false/true on success/failure
  */
  virtual bool
  GetAlbedo(CValues             &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType    distType = EE_ALL) const = 0;

  /**
     Returns Maximum albedo for a given BRDF model.

     The returned value should be between 0.0 and 1.0 and says how much
     of the incoming light from given direction is propagated (ref(le|ra)cted).
     That is, the probability of absorbtion is (1.0 - albedo).

     @param hitInfo(in) the point and incoming direction

     @param albedo the albedo in each spectral band

     @return false/true on success/failure
  */
  virtual bool
  GetAlbedoMax(CValues             &albedo,
	       const CHitPointInfo &hitInfo,
	       EDistributionType    distType = EE_ALL) const = 0;


  /**
     Returns the average diffusness for this particular surface over all
     input domain (2D mapping domain).

     @return number between 0 and 1, 1 for ideal diffuse, 0 for mirror
  */
  virtual float GetAverageDiffuseness(void) const=0;

  /**
     Get the diffuseness at the specified point taking into account only
     specified components. Diffuseness characterizes how narrow is the
     bundle of reflected/refracted rays (for Lambertian surface, diffuseness
     is equal to 1 and for mirror surface it is equal to 0)

     @param hitInfo(in)   point of interest
     @param distType(in)  reflection components

     @return Weighted average of diffuseness at specified components.
             The weight is components's albedo.
             
  */
  virtual float GetDiffuseness(const CHitPointInfo &hitinfo,
			       EDistributionType    distType = EE_ALL) const = 0;


  /**
     Returns the transparency at the specified point, possibly taking the
     incoming direction of the ray into account. Returns false if the
     surface is not completely opaque, if the surface is opaque, returns true.

     @param transparency output value computed by function in spectral domain

     @return false/true success/failure
  */
  virtual bool GetTransparency(CValues             &transparency,
			       const CHitPointInfo &hitinfo) const;

  /**
     Sets the 'default' color of the surface based on its type etc. This is
     intended to be used in physically based applications such as ray tracing
     with shaders that requires some default color. Returns false when the
     color has been found. Return true when default color cannot be set and
     in this case set the result to zero.

     @param defaultColor output color found for the surface

     @return hitInfo     the input point
  */
  virtual bool GetDefaultColor(CValues             &defaultColor,
			       const CHitPointInfo &hitInfo) const = 0;
  
  // returns the type of the surface
  virtual EType GetType() const = 0;

  EDistributionType GetSupportedTypes()
  { return (EDistributionType) _supportedType; }
  
  int IsTypeSupported(EDistributionType t)
  { return _supportedType & t; }
  
  // describes the surface to a stream
  virtual void Describe(ostream &, int) const { }

  // manipulation with BRDF model parameters - connection with possible GUI

  /**
     Returns the title of the parameter based on index of this parameter

     @param parameterIndex	index of the parameter whose title we want to know

     @return				string with the title of the parameter
  */
  virtual const char* 
  GetParameterTitle(int parameterIndex) const;

  /**
     Returns a description of the parameter based on index of this parameter.
	 It describes meaning of the target parameter in detail and its purpose and practice 
	 influence to appearance of the visualisation of the model.

     @param parameterIndex	index of the parameter whose description we want to know

     @return				string with the description of the parameter
  */
  virtual const char* 
  GetParameterDescription(int parameterIndex) const;

  /**
     Returns syntax of the parameter based on index of this parameter.
	 Syntax means type of the parameter and it helps programmer to dynamically
	 create GUI element for this parameter based on its type (e.g. R/G/B values
	 have different GUI inputs than some float parameter). So it helps programmer
	 to distinguish different types of parameters between each other. See
	 EParameterUIType for these parameter types.

	 @param parameterIndex	index of the parameter which syntax we want to know

     @return				string with the description of the model
  */
  virtual
  EParameterUIType GetParameterSyntax(int parameterIndex) const;

  /**
     Sets value of the target parameter

     @param parameterIndex(in)	index of the parameter which value we want to set
	 @param value(in)			value to set to the parameter
  */
  virtual void 
  SetParameter(int parameterIndex, const CValues &value);

   /**
     Gets current value of the target parameter

     @param parameterIndex(in)	index of the parameter which value we want to know

	 @return					value of the parameter
  */
  virtual CValues  
  GetParameter(int parameterIndex) const;

  /// Gets bounds of the parameter and its default value
  /**
     Gets minimum and maximum bound of the parameter and its initial value and
	 returns these values in the vector in this order : min,max,default

     @param parameterIndex(in)	index of the parameter whose contraints we want to get

	 @return					vector of contraints of the target parameter
  */
  virtual CValues  
  GetParameterConstraints(int parameterIndex) const;

  /**
     Gets the number of parameters of the current model

	 @return				parameters count
  */
  virtual int 
  GetNumParameters() const;

   /**
     Returns a string key to detailed description of the current BRDF model. Its an
	 unique id in a string form, which identifies a BRDF model. Based on this string key,
	 detailed description containing meaning of the model, its purpose, typical 
	 usability, etc. is displayed to the user and so it helps to better understand the 
	 purpose of the model.

     @return				string with the description of the model
  */
  virtual const char* GetHelp() const;
  


private:
  // forbid using the copy operator and assignment operator
  CSurface& operator=(const CSurface&);
  CSurface(const CSurface&);

protected:

  // The supported types of interaction are stored here
  int _supportedType;

  void _setSupportedType(EDistributionType t)
  {
    _supportedType |= t;
  }

  void _unsetSupportedType(EDistributionType t)
  {
    _supportedType &= ~t;
  }

  // For a now it is set to 10000
  static float _minExpAsMirror;
  /// convert phong exponent to diffuseness between o and 1

  // Actually, what is diffuseness? It is the degree of light scattering. 
  // In some sense the narrowness of cosine lobe
  // could be sonsidered as diffuseness. Diffuse component is
  // the particular case of specular exponentional component, when
  // shininess = 0. For diffuse interaction, light is scattered over
  // the whole hemisphere => diffuseness => Shemisphere/S_hemisphere
  inline float _nToDiffuseness(float n) const
  {
    if (n <= 1.0)
      return 1.0;
    if (n >= _minExpAsMirror)
      return 0.0;
    return 1.0 - log(n) / log(_minExpAsMirror);
  }  
  /// Auxiliary method for standard constraints setup, based on the type of the parameter.
  inline void _setBasicConstraints(EParameterUIType type, CValues& val) const
  {
	  switch(type)
	  {
	  case EE_ColorRGB:
		    val = CValues(9);
			val.Set(0,0); // min R
			val.Set(1,1); // max R
			val.Set(2,0.5f); // default R
			val.Set(3,0); // min G
			val.Set(4,1); // max G
			val.Set(5,0.5f); // default G
			val.Set(6,0); // min B
			val.Set(7,1); // max B
			val.Set(8,0.5f); // default B
			break;
	  case EE_Weighted_Float:
		    val = CValues(6);
			val.Set(0,0); // min value
			val.Set(1,1); // max value
			val.Set(2,0.5f); // default value
			val.Set(3,0); // min weight
			val.Set(4,1); // max weight
			val.Set(5,0); // default weight
			break;
	  case EE_Weighted_XYZ:
		    val = CValues(12);
			val.Set(0,0); // min X
			val.Set(1,1); // max X
			val.Set(2,1); // default X
			val.Set(3,0); // min Y
			val.Set(4,1); // max Y
			val.Set(5,0); // default Y
			val.Set(6,0); // min Z
			val.Set(7,1); // max Z
			val.Set(8,0); // default Z
			val.Set(9,0); // min weight
			val.Set(10,1); // max weight
			val.Set(11,0); // default weight
			break;
	   case EE_RGB_Components:
		    val = CValues(9);
			val.Set(0,0); // min R
			val.Set(1,MAX_SHININESS); // max R
			val.Set(2,1); // default R
			val.Set(3,0); // min G
			val.Set(4,MAX_SHININESS); // max G
			val.Set(5,1); // default G
			val.Set(6,0); // min B
			val.Set(7,MAX_SHININESS); // max B
			val.Set(8,1); // default B
			break;
	  case EE_Boolean:
		    val.SetRepresentation(CValues::EE_Weigth3);
			val.Set(0,0); // min = false
			val.Set(1,1); // max = true
			val.Set(2,0); // default - false
			break;
	  case EE_Fresnel:
		    val = CValues(18);
			val.Set(0,0); // color min R
			val.Set(1,1); // color max R
			val.Set(2,0.5f); // color default R
			val.Set(3,0); // color min G
			val.Set(4,1); // color max G
			val.Set(5,0.5f); // color default G
			val.Set(6,0); // color min B
			val.Set(7,1); // color max B
			val.Set(8,0.5f); // color default B
			val.Set(9,1); // ior min
			val.Set(10,MAX_IOR); // ior max
			val.Set(11,1); // default ior
			val.Set(12,0); // extinction coef min
			val.Set(13,MAX_EXTCOEF); // extinction coef max
			val.Set(14,0.5f); // default extinction coef
			val.Set(15,0); // schlick approx min (false)
			val.Set(16,1); // schlick approx max (true)
			val.Set(17,0); // schlick approx defaultly false
			break;
	  case EE_Layer:
		    val = CValues(12);
			val.Set(0,0.01f); // thickness min
			val.Set(1,20); // thickness max
			val.Set(2,1); // thickness default
			val.Set(3,0); // absorbtion R min
			val.Set(4,1); // absorbtion R max
			val.Set(5,0.5f); // absorbtion R default
			val.Set(6,0); // absorbtion G min
			val.Set(7,1); // absorbtion G max
			val.Set(8,0.5f); // absorbtion G default
			val.Set(9,0); // absorbtion B min
			val.Set(10,1); // absorbtion B max
			val.Set(11,0.5f); // absorbtion B default
			break;
	  default: break;
	  }
  }
};

__END_BRDFLIB_HEADER

#endif // __SURFACE_H__

