#include "config.h"
#include "neumann.h"
#include "texture.h"
#include "functions.h"

__BEGIN_BRDFLIB_SOURCE

typedef vector<double> rnd_array;
rnd_array CSurfaceNeumann::diffuse_rnd;
rnd_array CSurfaceNeumann::specular_rnd;

// describes the surface to a given stream
//========================================================================
void
CSurfaceNeumann::Describe(ostream &app, int ind) const
{
	indent(app, ind);
	app << GetName() << ":\n";

	if (_diffuse) {
		indent(app, ind+1);
		app << "  diffuse  = \t";
		_diffuse->Describe(app, ind); app << "\n";
	}

	if (_specular) {
		indent(app, ind+1);
		app << "  specular  = \t";
		_specular->Describe(app, ind); app << "\n";
	}

	if (_shininess) {
		indent(app, ind+1);
		app << "  specular  = \t";
		_shininess->Describe(app, ind); app << "\n";
	}

  app << flush;
  return;
}

/*
  
*/
//========================================================================
bool
CSurfaceNeumann::GetAlbedo(CValues &albedo,
                          const CHitPointInfo &hitInfo,
                          EDistributionType   distType) const
{
  //CVector3D N       = hitInfo->GetNormal();
  //CVector3D dirInit = hitInfo->GetRay()->GetDir();

  // As default for all the Microfacet-based models. Actually, for each
  // model albedo is a function of the angle bewtween the incoming
  // direction and normal.
  CValues c(CValues::EE_Weigth3); // result - zero by default
  CValues d(CValues::EE_Weigth3);
  bool retval = false;

  if ((distType & EE_REFLECTED_DIFFUSE) && _diffuse) {
    hitInfo.GetTextureValue(d, *_diffuse, CMappingBase::EE_KD);
    c += d;
    retval = true;
  }

  if ((distType & EE_REFLECTED_SPECULAR) && _specular) {
    hitInfo.GetTextureValue(d, *_specular, CMappingBase::EE_KS);
    c += d;
    retval = true;
  }

  if (retval) {
    albedo = c;
    return true;
  }

  return false;
}


//========================================================================
// according to original paper ref[1] - eq (55) :
//
//         kd
// BRDF = ---- + (1-kd) * Cmax * Xi(m) * F(thetaHP)
//         Pi
//
// where :
//		- for metals kd == 0, for plastics kd > 0
//		- kd is diffuse albedo
//		- F is Frensel term computed for specular part of the surface for thetaHP
//		- thetaHP = arcsin(|HP - origin|)
//			- |..| = distance(magnitude)
//			- HP = projected unit half vector onto the surface plane (perp. to normal)
//			- origin = hitpoint
//			- thetaHP = arcsin(|g-(N.g)N|/2)
//				- N is surface normal
//				- g = (L - V)
//					- L = light direction
//					- V = viewer direction
//		- Xi(m) = e^(-s*m)
//			- s = shinniness
//			- m = |h - (N.h)N|
//				- |..| = distance(magnitude)
//				- N is surface normal
//				- h is unnormalized half vector (L + V)
//				
//						s^2
//		- Cmax = ----------------------
//				  2*Pi(1-(1+s)*e^(-s))
//
//			- note, that for s > 10 it simplifies to s^2/(2*Pi)
// ----------------------------------------------------------------------------

bool
CSurfaceNeumann::EvaluateBSDF(CValues &result,
			 const CHitPointInfo &hitInfo,
			 const CVector3D     &dirIn,
			 EDistributionType   distType) const
{
	bool retval = false;
	float d = Magnitude(dirIn);
	CVector3D N          =  hitInfo.GetNormal();
	CVector3D anisAxis   =  hitInfo.GetAnisAxis();

	assert(fabs(DotProd(N, anisAxis)) < 1e-06); // pi/2

	float lightCos       =  DotProd(dirIn, N);
	float incEyeCos  =  -(hitInfo.GetDotProd());

	result.Set(0);

	if(lightCos >= -WTINY && incEyeCos >= -WTINY)
	{
		if(lightCos < 0) lightCos = 0;
		else
		{
			if(lightCos < WTINY) lightCos       = WTINY;
		}

		if(incEyeCos < 0) incEyeCos = 0;
		else
		{
			if(incEyeCos < WTINY) incEyeCos  = WTINY;
		}

		if ( (d < WTINY1L) || (d > WTINY1G) )
		{
			Normalize(dirIn);
		}
		float comp = 1.0;

		if(_diffuse && (distType & EE_REFLECTED_DIFFUSE)) 
		{
			CValues rho_d;
			hitInfo.GetTextureValue(rho_d, *_diffuse, CMappingBase::EE_KD);
			result += rho_d * M_1_PI;
			comp = 1.0 - rho_d.Luminance(); // complement to 1.0
			retval = true;
		}
		
		
		if(_specular && (distType & EE_REFLECTED_DIFFUSE) && _shininess) 
		{
			if (!_specular->IsFresnel())
			{
				WARNING_INFO("CSurfaceNeumann::EvaluateBSDF() :\nSpecular part has to be of Fresnel type!");
				return false;
			}
			
			CValues sval;
			hitInfo.GetTextureValue(sval, *_shininess, CMappingBase::EE_KSI);

			float s = sval.Get(0);

			// Cmax
			float cmax = EvaluateCmax(s);

			CVector3D V = -hitInfo.GetRay()->GetDir();

			// Xi(m) = e^(-s*m)
			CVector3D h = dirIn + V;
			float cos_hN = DotProd(h, N);
			assert(cos_hN >= 0);
			if(cos_hN < WTINY)  cos_hN = WTINY;
			float m = Magnitude(h - cos_hN*N);
			float xi_m = exp(-s*m);

			// F(thetaHP)
			CVector3D g = dirIn - V;
			float cos_gN = DotProd(g, N);
			float sin_hp = Magnitude(g- cos_gN*N)/2.f;
			CValues F;
			// arbitrary fresnel computation expects cos(theta) as the last parameter, not theta
			float cos_hp = sqrt(1.f - sin_hp*sin_hp);
			hitInfo.GetTextureValue(F,  *_specular, CMappingBase::EE_KFR, cos_hp);
			
			result += comp * cmax * xi_m * F;
			retval = true;
		}

		if(retval)
		{
			result *= lightCos;
		}
	} // not under the surface
	else // under the surface
	{
		retval = false;
	}
	return retval;
}


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


int
CSurfaceNeumann::SpawnStochasticRays(CContributionArray  &carray,
							const CHitPointInfo &hitInfo,
                            CSamplingInfo       &samplingInfo,
							EDistributionType	distType) const
{
	int maxRays = samplingInfo.GetMaxSamples();
	assert(maxRays > 0);

	diffuse_rnd.clear();
	specular_rnd.clear();

	// store the seed to remain the same if possible, depending
	// on the random generator.
	samplingInfo.StoreSeed();

	// diffuse component
	CValues kd_local(CValues::EE_Weigth3);
	// specular component
	CValues ks_local(CValues::EE_Weigth3);

	// compute diffuse and specular albedos
	float akd = 0.0f, aks = 0.0f;

	// validate hitInfo.dotprod
	hitInfo.GetNormal();

	// compute albedo for diffuse part
	//----------------------------------------------------------------------
	if (_diffuse && (distType & EE_REFLECTED_DIFFUSE)) {
		hitInfo.GetTextureValue(kd_local, *_diffuse, CMappingBase::EE_KD);
		akd = kd_local.Luminance();
	}

	// compute albedo for specular part
	//----------------------------------------------------------------------
	if (_specular && (distType & EE_REFLECTED_SPECULAR)) {
		hitInfo.GetTextureValue(ks_local, *_specular, CMappingBase::EE_KS);
		aks = ks_local.Luminance();
	}

	// Complete albedo of Phong with mirror reflection at grazing angles model
	//----------------------------------------------------------------------
	float albedo_whole = akd + aks;

	if(albedo_whole < 1e-05) {
		WARNING_INFO("CSurfacePhong::SpawnStochasticRays() :\n\
Albedo of this surface < 1e-05 => it is the black body!");
		return 0;
	}

	if( albedo_whole >= 1.0 ) {
		WARNING_INFO("CSurfacePhong::SpawnStochasticRays():\nAlbedo > 1.0\n\
To keep the energy conservation requirement the total albedo\n\
(albedo_specularity + albedo_diffuse + albedo_transmittance) should be less than 1.\n\
(If it is equal to 1, termination of a random walk with\nRussian Roulette will be impossible)");
	}

	// Compute the number of rays to reflect diffusely / specularly
	int numDiffuse = 0;
	int numSpecular = 0;

	int count = maxRays;
	while(count--)
	{
		float rnd_num = samplingInfo.Sample(0);

		// This RND number is from the User's program. Actually, it should be
		// fit into the range 0..albedo_whole if some event besides absorbtion
		// was chosen. Therefore, we should make RND fall into the [0..1)
		if(count >= maxRays-1)
		{
			if(rnd_num <= albedo_whole)
				rnd_num /= albedo_whole;  // make first RND fall into the [0..1)
		}
		assert(rnd_num <= 1.0);

		float alpha1 = akd/albedo_whole;
		float alpha2 = aks/albedo_whole;

		if((rnd_num < (alpha1) && alpha1 > 1e-6)) 
		{
			// Reuse random number for generation of one component of the random
			// direction. Scale it to fall into the range [0..1)
			diffuse_rnd.push_back(rnd_num/alpha1);
			// Choose another component of direction randomly
			diffuse_rnd.push_back(samplingInfo.Sample(1));
			numDiffuse++;
		}
		else 
		{
			// Reuse random number for generation of one component of the random
			// direction. Scale it to fall into the range [0..1)
			specular_rnd.push_back((rnd_num-alpha1)/alpha2);
			// Choose another component of direction randomly
			specular_rnd.push_back(samplingInfo.Sample(1));
			numSpecular++;
		}
		
		// For a now it does not work for Halton sequence.
		samplingInfo.IncDimension(2);
		samplingInfo.RecoverSeed();
	} // while count

	int numRays = 0;

	CValues shininess;
	hitInfo.GetTextureValue(shininess, *_shininess, CMappingBase::EE_KSI);

	// The number of rays for diffuse interaction
	//----------------------------------------------------------------------
	if (numDiffuse) {
	numRays += SpawnStochasticDiffuseRays(kd_local, shininess, carray,
					  hitInfo, samplingInfo,
					  numDiffuse);
	}

	// The number of rays for diffuse interaction
	int indexDiffuse = carray.GetUsedItems();

	// The number of rays for specular interaction
	//----------------------------------------------------------------------
	if (numSpecular) {
	numRays += SpawnStochasticSpecularRays(ks_local, shininess, carray,
					   hitInfo, samplingInfo,
					   numSpecular);
	}
	int indexMax = carray.GetUsedItems();
	assert(indexMax > 0);

	samplingInfo.IncDimension(2);

	// recover the seed for the next dimension to be sampled
	samplingInfo.RecoverSeed();

	assert(indexMax == maxRays);
	return numRays;
}

//========================================================================
// Specular part for stochastic sampling according to original paper [1] 
//
//========================================================================
int
CSurfaceNeumann::SpawnStochasticSpecularRays(
  const CValues       &kd_local,
  const CValues       &s_local,
  CContributionArray  &carray,
  const CHitPointInfo &hitInfo,
  CSamplingInfo       &samplingInfo,
  int                 maxRays) const
{
	int num = 0; // the number of samples generated

	CVector3D N = hitInfo.GetNormal();
	if (hitInfo.GetDotProd() > 0.0) // for refraction
	{
		N = -N;
	}

	// convert V to local coordinate system
	CVector3D V = -hitInfo.GetRay()->GetDir();

	// diffuseness of the bounce
	float diffuseness = 1; //_nToDiffuseness(s_local.Sum() / s_local.GetCount());

	float s = s_local.Get(0);
	float R = EvaluateR(s,specular_rnd[0]);

	// Compute directions for maxRays outgoing rays
	//----------------------------------------------------------------------
	for(int i = maxRays; i--; num++) 
	{	 
		CVector3D local_L;

		float w = 0;

		if (R < WTINY)
		{
			// perfect mirror reflection
			local_L = hitInfo.GetReflectedDir();
			// of weight 1
			w = 1;
		}
		else
		{
			// returns albedo = weight of the sample
			w = GenerateLocalDirWithBasicBRDF(R,V,N,local_L);
		}

		CVector3D dirRes = local_L;

		float lightCos = DotProd(N, dirRes);
		// the ray is sampled in this direction
		CContributionItem *contrib = carray.AllocateNext();
		contrib->weigth.SetRepresentation(CValues::EE_Weigth3);

		float c = pow(s,3) / 2.f; 
		// for s > ~10, (1 - (1 + s)*e^(-s)) evaluates to 1
		if (s < 10)
		{
			c /= (1.f - (1.f + s)*exp(-s));
		}
		// compute the probability density
		contrib->prob = c * exp(-s*R) * Min(1.f,R*R);

		// Set the value of BSDF type
		contrib->weigth.SetRepresentation(CValues::EE_Weigth3);
		
		CValues F;
		hitInfo.GetTextureValue(F,  *_specular, CMappingBase::EE_KFR);
		
		contrib->weigth = w * F;

		// set the reflected ray, direction is normalized here
		contrib->ray.Init(hitInfo.GetPoint(), dirRes, REFLECTED_RAY);
		// set the flags concerning ray type
		contrib->SetFlags(CContributionItem::EE_ReflectedRay);
		// type of interaction
		contrib->interaction = EE_REFLECTED_SPECULAR;
		// diffuseness of the bounce
		contrib->diffuseness = diffuseness;

		// Additional (diffuse) components in order to provide nice and simple
		// visualisation
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		if(kd_local.Luminance() > 1e-05) 
		{
			//CValues dif = kd_local * (1.0/(M_PI*contrib->prob));
			//contrib->weigth += dif;
		}
		// Cosine between the incoming ray and the normal does not taken
		// into account.
		contrib->weigth *= lightCos;

		#ifdef __TRAVERSAL_STATISTICS
		world->_statistics->IncReflectedRays();
		#endif
		
	} // for i

	// return number of generated samples
	assert(num == maxRays);
	return num;
}
//========================================================================

float
CSurfaceNeumann::GenerateLocalDirWithBasicBRDF(float		R, 
										  const CVector3D&	V, 
										  const CVector3D&	N, 
										  CVector3D&		L) const
{
	// projection of reflected ray
	CVector3D Vp = -(V - DotProd(N,V)*N);
	
	// be sure that there is ANY common area between
	// unit circle with center in 0 and circle with center Vp
	// and radius R. If not, something is wrong
	assert(Magnitude(Vp) < 1+R-WTINY);

	bool valid = false;
	float r1,r2;
	// projection of generated ray
	CVector3D Lp;
	if (R < 1)
	{
		while (!valid)
		{
			// generate Lp within C(Vp,R) = generate Lp within radius R
			// and shift it by Vp
			// point within radius R = generate two random numbers (position in 
			// a square that encloses this circle. if point is laying outside this
			// circle (is distant from the origin > R), generate another one..
			// until point lies inside the circle
			r1 = RandomValue(-R,R);
			r2 = RandomValue(-R,R);

			float a = Magnitude(Vp);
			// 2D circle in the plane XZ has Y == 0
			// assuming, that normal is (0,0,1)
			Lp.SetValue(r1,r2,0);
			if (Magnitude(Lp) > R)
			{
				continue;
			}
			else // point lies within radius R
			{
				// shift point by Vp to the circle with the center of Vp
				Lp += Vp;
			}
			if (Magnitude(Lp) >= 1.f) // point lies outside the unit circle, reject it
			{
				continue;
			}
			valid = true;
		}
	}
	else // R > 1
	{
		while (!valid)
		{
			// similar ray generation..but now we generate first in the unit circle
			// and then test it with the given circle C(Vp,R)
			r1 = RandomValue(-1.f,1.f); // within unit circle : (-1) - 1
			r2 = RandomValue(-1.f,1.f); // within unit circle : (-1) - 1
			Lp.SetValue(r1,r2,0);
			if (Magnitude(Lp) > 1.f)
			{
				continue;
			}
			else // point lies within unit radius
			{
				
			}
			if (Magnitude(Lp - Vp) >= R) // point lies outside the given circle, reject it
			{
				continue;
			}
			valid = true;
		}
	}
	L = Lp + sqrt(1.f - DotProd(Lp,Lp)) * N;
	L.Normalize();
	float a = 1; // in the paper : a = C(Vp,R) / (M_PI * Min(1.f,R*R)) ?????; maybe it should be Area(C(Vp,R)) / ...
	return a;
}

//========================================================================
int
CSurfaceNeumann::SpawnStochasticDiffuseRays(
  const CValues       &kd_local,
  const CValues       &s_local,
  CContributionArray  &carray,
  const CHitPointInfo &hitInfo,
  CSamplingInfo       &samplingInfo,
  int                 maxRays) const
{
  CVector3D N = hitInfo.GetNormal();
  if (hitInfo.GetDotProd() > 0.0)
  {
    N = -N;
  }
  CVector3D U = ArbitraryNormal(N);     // U is perpendicular to N
  CVector3D V = CrossProd(N, U);        // orthonormal base with N and U

  int num = 0;							// number of rays created

  // Compute data for maxRays outgoing rays
  //----------------------------------------------------------------------
  for(int i = maxRays; i--; num++) {
     // allocate next contribution
    CContributionItem *contrib = carray.AllocateNext();
    // generate the sample in the cannonical space
    CVector3D dirN;
    // Generate a sample in 2D, range [0;1)x[0;1)
    CVector2D vv(diffuse_rnd[2*num], diffuse_rnd[2*num+1]);

    // probability is set to value cos(theta)/M_PI
    contrib->prob = RND::hemisph_cos(dirN, vv);
    // transform to real coordinates
    CVector3D dirRes = dirN.x * U + dirN.y * V + dirN.z * N ;
    // set the reflected ray
    contrib->ray.Init(hitInfo.GetPoint(), dirRes, REFLECTED_RAY);
    // set the flags concerning ray type
    contrib->SetFlags(CContributionItem::EE_ReflectedRay);
    // type of interaction
    contrib->interaction = EE_REFLECTED_DIFFUSE;
    // diffuseness of the bounce
    contrib->diffuseness = 1.0;
    // Set the value of BSDF type
    // compute = BRDF * cos(normal, ray.dir) / pdf =
    //         = pd/Pi * cos(normal, ray.dir) /(cos(normal, ray.dir)/Pi)
    //         = pd
    // This means that we should not multiply the weight by the cosine
    // of the angle between the incoming light direction and normal
    contrib->weigth.SetRepresentation(CValues::EE_Weigth3);
    contrib->weigth = kd_local;

    // Additional (specular) component in order to provide nice and simple
    // visualization
    //------------------------------------------------------------------------
    if (_specular) 
	{
      if (!_specular->IsFresnel())
			{
				WARNING_INFO("CSurfaceNeumann::SpawnStochasticDiffuseRays() :\nSpecular part has to be of Fresnel type!");
				return 0;
			}

			float s = s_local.Get(0);

			// Cmax
			float cmax = EvaluateCmax(s);

			CVector3D V = -hitInfo.GetRay()->GetDir();

			// Xi(m) = e^(-s*m)
			CVector3D h = dirRes + V;
			float cos_hN = DotProd(h, N);
			assert(cos_hN >= 0);
			if(cos_hN < WTINY)  cos_hN = WTINY;
			float m = Magnitude(h - cos_hN*N);
			float xi_m = exp(-s*m);

			// F(thetaHP)
			CVector3D g = dirRes - V;
			float cos_gN = DotProd(g, N);
			float sin_hp = Magnitude(g- cos_gN*N)/2.f;
			CValues F;
			// arbitrary fresnel computation expects cos(theta) as the last parameter, not theta
			float cos_hp = sqrt(1.f - sin_hp*sin_hp);
			hitInfo.GetTextureValue(F,  *_specular, CMappingBase::EE_KFR, cos_hp);
			
			CValues spec_w = (1.f - kd_local.Luminance()) * cmax * xi_m * F;
            float lightCos = DotProd(N, dirRes);

		    contrib->weigth += (spec_w*lightCos) * (1.f/contrib->prob);
	}

#ifdef __TRAVERSAL_STATISTICS
    world->_statistics->IncReflectedRays();
#endif

  } // for i
  return maxRays;
}
//========================================================================

float
CSurfaceNeumann::EvaluateCmax(float shinniness) const
{
	float cmax = shinniness*shinniness / (2.f * M_PI);
	if (shinniness < 10)
	{
		cmax /= (1.f - (1.f+shinniness)*exp(-shinniness));
	}
	return cmax;
}

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

float
CSurfaceNeumann::EvaluateR(float shininess, float rndNum) const
{
	// according to original paper :
	// R = F^-1(r)  (page 8)
	// where r is random number in range <0,1>
	// and F^-1 is inverse of the F(R)
	// where 
	// F(R) = Integral[0->R] p(t) dt = (e^-(s*t) * c) dt    ...(eq. 47)
	// where        
	//						  s^3
	//			c = --------------------------
	//				 2 * (1 - (1 + s)*e^(-s))
	//
	// and s is shininess
	//												   R
	// this Integral equals to F(R) = [-c/s * e^(-s*t)]  = -c/s * (e^(-s*R) - 1)
	//												   0								
	//									ln (1-R*(s/c))
	// from which inverse is F^-1(R) = ----------------
	//										  -s
	//
	// note..because there is a Max(1,R^2) in the original p(t) = ... (e.47),
	// we should split into two branches.. for R <= 1 (a) and for R > 1 (b)
	// For (a) integral is hard (but computable), but inverse of it is very complex
	// and even inverse function solvers (e.g. 
	// http://www.numberempire.com/inversefunctioncalculator.php 
	// cant solve it. SO..we take only the option (b) to solve.. Original paper
	// has nice plot of this inverse function based on random number r, but its 
	// analytical formula is missing :X For this time, we must take at least this solution..
	//
	// Ok, lets back to the R evaluating.. lets evaluate F^-1(r) for given random number

	float c = pow(shininess,3) / 2.f; 
	// for s > ~10, (1 - (1 + s)*e^(-s)) evaluates to 1
	if (shininess < 10)
	{
		c /= (1.f - (1.f + shininess)*exp(-shininess));
	}
	return (log(1.f - rndNum*(shininess/c))) / -shininess;
}									

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

int 
CSurfaceNeumann::GetNumParameters() const
{
	return 3; // diffuse, specular, s
}
//========================================================================

CValues
CSurfaceNeumann::GetParameter(int parameterIndex) const
{
	CValues c(0,0,0);
	switch (parameterIndex)
	{
		case 0 :
			_diffuse->GetValue(c,CHitPointInfo::constMapInfo);
			break;
		case 1 :
			_specular->GetValue(c,CHitPointInfo::constMapInfo);
			break;
		case 2 :
			_shininess->GetValue(c,CHitPointInfo::constMapInfo);
			break;
	}
	return c;
}
//========================================================================

CValues
CSurfaceNeumann::GetParameterConstraints(int parameterIndex) const
{
	CValues c(CValues::EE_WeigthN);
	switch (parameterIndex)
	{
		case 0 :
			_setBasicConstraints(EE_ColorRGB,c);
		case 1 :
			_setBasicConstraints(EE_Fresnel,c);
			break;
		case 2 :
			c.SetRepresentation(CValues::EE_Weigth3);
			c.Set(0,0); // min shininess = 0
			c.Set(1,MAX_SHININESS); // max shininess
			c.Set(2,10); // default
			break;
	}
	return c;
}
//========================================================================

const char* 
CSurfaceNeumann::GetParameterDescription(int parameterIndex) const
{
	switch (parameterIndex)
	{
	case 0 :
		return BrdfStrings::KDiffuseDesc;
	case 1 :
		return BrdfStrings::KFresnelDesc;
	case 2 :
		return BrdfStrings::KShininessDesc;
	default :
		return BrdfStrings::KInvalidParameter;
	}
}
//========================================================================

CSurface::EParameterUIType
CSurfaceNeumann::GetParameterSyntax(int parameterIndex) const
{
	switch (parameterIndex)
	{
	case 0 : // diffuse 
		return EE_ColorRGB;
	case 1 : // specular 
		return EE_Fresnel;
	case 2 : // shininess
		return EE_Float;
	default :
		return EE_Unknown;
	}
}
//========================================================================

const char*
CSurfaceNeumann::GetParameterTitle(int parameterIndex) const
{
	switch (parameterIndex)
	{
	case 0 :
		return BrdfStrings::KDiffuseTitle;
	case 1 :
		return BrdfStrings::KFresnelTitle;
	case 2 :
		return BrdfStrings::KShininessTitle;
	default :
		return BrdfStrings::KInvalidParameter;
	}
}
//========================================================================

void
CSurfaceNeumann::SetParameter(int parameterIndex, const CValues& value)
{

	switch (parameterIndex)
	{
	case 0 :
		{
			CConstantTexture* newTex = new CConstantTexture(value);
			SetKd(newTex);
			break;
		}
	case 1 :
		{
			if (!_specular)
			{
				_specular = new CFresnelTexture(CValues(CValues::EE_Weigth3));
			}
			CFresnelTexture* tex = dynamic_cast<CFresnelTexture*>(_specular);
			CValues auxVal(CValues::EE_Weigth3);
			auxVal.Set(0,value.Get(0));
			auxVal.Set(1,value.Get(1));
			auxVal.Set(2,value.Get(2));
			tex->SetColor(auxVal);
			auxVal.SetRepresentation(CValues::EE_Weigth1);
			auxVal.Set(value.Get(3));
			tex->SetIndexR(auxVal);
			auxVal.Set(value.Get(4));
			tex->SetExtinction(auxVal);
			bool useSchlick = value.Get(5);
			tex->SetApproxSchlick(useSchlick);
			break;
		}
	case 2 :
		{
			CConstantTexture* newTex = new CConstantTexture(value);
			SetShininess(newTex);
			break;
		}
	}
}
//========================================================================

const char* 
CSurfaceNeumann::GetHelp() const
{
	return BrdfStrings::KNeumannDesc;
}

__END_BRDFLIB_SOURCE