// ===================================================================
// $Id$
//
// oren_nayar.h
//    Implementation of Oren Nayar BRDF surface.
//
// Class: CSurfaceOrenNayar
//
// Initial coding by Jiri Nekola within diploma thesis, November 2011

// Reference:
//  1. Oren, M. Nayar, S.K. "Generalization of Lambert’s Reflectance Model",
//     SIGGRAPH, 1994
//  2. Neumann, A. Ph.D. "Construction of Bidirectional Reflection 
//     Distribution Functions", 2001
//  3. Schwenk, K. "A Survey of Shading Models for Real-time Rendering", 
//	   2008


#ifndef __SURFACE_OREN_NAYAR_H__
#define __SURFACE_OREN_NAYAR_H__

#include "configh.h"
#include "surface.h"
#include "surface_microfacet.h"

__BEGIN_BRDFLIB_HEADER

#define K05		0.5
#define K033	0.33
#define K045	0.45
#define K009	0.09

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

// ------------------------------------------------------------------------------
/// Oren Nayar BRDF class implemented according to Oren-Nayar original paper &
/// Neumann phd thesis
/// Despite of its slope-based surface, it is not inherited from CSurfaceMicrofacet,
/// because that is designed to be used for surfaces with specular component and
/// Oren Nayar has only diffuse component + slope.
// ------------------------------------------------------------------------------
class CSurfaceOrenNayar :
  public CSurface
{
public:
	/**
		 Constructs the Oren Nayar surface.
		 @param rho_d (in)  The diffuse reflectance texture.
		 @param slope (in)  Standard deviation (RMS) of the surface slope.
	*/
	CSurfaceOrenNayar(CTexture *rho_d, CSlopeMicrofacets *slope):
	  CSurface(EE_NONE)
	{
		if(rho_d != NULL)
		{
			_diffuse  = rho_d;
			_setSupportedType(EE_REFLECTED_DIFFUSE);
		}

		if(slope && slope->GetSlopesSize() == 1)
		{
			_slope = slope;
		}
		else
		{
			WARNING_INFO("CSurfaceOrenNayar : this surface needs exactly one slope structure!");
			return;
		}
	}

	CSurfaceOrenNayar():
		CSurface(EE_NONE),
		_diffuse(0),
		_slope(0)
	{
	}

	virtual ~CSurfaceOrenNayar() 
	{
		if (_diffuse)
		{
			delete _diffuse;
		}
		if (_slope)
		{
			delete _slope;
		}
	}

	/**
	 Returns the value of diffuse coefficient at given point.
	 (kd is albedo)
	*/
	inline CValues kd(const CHitPointInfo &hitInfo) const
	{
		CValues ret(CValues::EE_Weigth3);
		hitInfo.GetTextureValue(ret, *_diffuse, CMappingBase::EE_KD);
		return ret;
	}

	inline void SetKd(CTexture* kd)
	{
		AssignPointer(_diffuse,kd); 
	}

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

	/**
		Deep copy this object.
	*/
	virtual CSurface *Dup() const
	{
		CValues value;
		_diffuse->GetValue(value,CHitPointInfo::constMapInfo);
		CConstantTexture* diffuse = new CConstantTexture(value);

		_slope->GetSlope(0)->GetValue(value,CHitPointInfo::constMapInfo);
		CConstantTexture* slope = new CConstantTexture(value);
		CSlopeMicrofacets* slopes = new CSlopeMicrofacets();
		slopes->Set(slope,0);
		slopes->SetWeight(1.0,0);

		return new CSurfaceOrenNayar(diffuse,slopes);
	}

	virtual bool
	GetAlbedoMax(CValues &albedo,
		const CHitPointInfo &hitInfo,
		EDistributionType   distType = EE_ALL) const
	{
		assert(_diffuse);
		if (distType & EE_REFLECTED_DIFFUSE) {
		  albedo = kd(hitInfo);
		  return true;
		}
		return false;
	}

	virtual bool GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) const	   
	{
		// let us consider _diffuse color to be the default one
		if (_diffuse) 
		{
			hitInfo.GetTextureValue(defaultColor, *_diffuse, CMappingBase::EE_KD);
			return false;
		}
		defaultColor.Set(0.0);
		return true;
	}

	virtual char* GetName() const
	{return "CSurfaceOrenNayar";}

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

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

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

	virtual float GetAverageDiffuseness(void) const  {
		WARNING_INFO("No idea how to estimate diffusenes of the Oren-Nayar model.\n\
It is a function of slope.");
		return 0.0;
	}

	virtual float GetDiffuseness(const CHitPointInfo &,
			       EDistributionType distType = EE_ALL) const
	{
		WARNING_INFO("No idea how to estimate diffusenes of the Oren-Nayar model.\n\
It is a function of slope.");
		return 0.0;
	}

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

	virtual inline EType GetType() const { return  EE_BSDFMicrofacetRefl; }

	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:
	CTexture			*_diffuse;	// Diffuse color of the object
	CSlopeMicrofacets	*_slope;	// standard deviation of the surface slope

	virtual EDistributionType _microfacetReflectionType() const
	{ return EE_REFLECTED_DIFFUSE; }
	// Function that spawns the rays using stratified sampling around a
	// given hemisphere. Can be used for reflection and for refraction
	// (just by inverting the passed normal).
	// distType MUST be either EE_REFLECTED_DIFFUSE or EE_REFRACTED_DIFFUSE
	int SpawnStochasticRaysStratify(CContributionArray  &carray,
				  const CHitPointInfo &hitInfo,
				  EDistributionType distType,
				  CSamplingInfo       &samplingInfo,
				  const CVector3D &N) const;

};

__END_BRDFLIB_HEADER

#endif // __SURFACE_OREN_NAYAR_H__