#include <string>
namespace slg { namespace ocl {
std::string KernelSource_material_funcs = 
"#line 2 \"material_funcs.cl\"\n"
"\n"
"/***************************************************************************\n"
" *   Copyright (C) 1998-2013 by authors (see AUTHORS.txt)                  *\n"
" *                                                                         *\n"
" *   This file is part of LuxRays.                                         *\n"
" *                                                                         *\n"
" *   LuxRays is free software; you can redistribute it and/or modify       *\n"
" *   it under the terms of the GNU General Public License as published by  *\n"
" *   the Free Software Foundation; either version 3 of the License, or     *\n"
" *   (at your option) any later version.                                   *\n"
" *                                                                         *\n"
" *   LuxRays is distributed in the hope that it will be useful,            *\n"
" *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *\n"
" *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *\n"
" *   GNU General Public License for more details.                          *\n"
" *                                                                         *\n"
" *   You should have received a copy of the GNU General Public License     *\n"
" *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *\n"
" *                                                                         *\n"
" *   LuxRays website: http://www.luxrender.net                             *\n"
" ***************************************************************************/\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Generic material related functions\n"
"//------------------------------------------------------------------------------\n"
"\n"
"float SchlickDistribution_SchlickZ(const float roughness, float cosNH) {\n"
"	const float d = 1.f + (roughness - 1.f) * cosNH * cosNH;\n"
"	return (roughness > 0.f) ? (roughness / (d * d)) : INFINITY;\n"
"}\n"
"\n"
"float SchlickDistribution_SchlickA(const float3 H, const float anisotropy) {\n"
"	const float h = sqrt(H.x * H.x + H.y * H.y);\n"
"	if (h > 0.f) {\n"
"		const float w = (anisotropy > 0.f ? H.x : H.y) / h;\n"
"		const float p = 1.f - fabs(anisotropy);\n"
"		return sqrt(p / (p * p + w * w * (1.f - p * p)));\n"
"	}\n"
"\n"
"	return 1.f;\n"
"}\n"
"\n"
"float SchlickDistribution_D(const float roughness, const float3 wh, const float anisotropy) {\n"
"	const float cosTheta = fabs(wh.z);\n"
"	return SchlickDistribution_SchlickZ(roughness, cosTheta) * SchlickDistribution_SchlickA(wh, anisotropy) * M_1_PI_F;\n"
"}\n"
"\n"
"float SchlickDistribution_SchlickG(const float roughness, const float costheta) {\n"
"	return costheta / (costheta * (1.f - roughness) + roughness);\n"
"}\n"
"\n"
"float SchlickDistribution_G(const float roughness, const float3 fixedDir, const float3 sampledDir) {\n"
"	return SchlickDistribution_SchlickG(roughness, fabs(fixedDir.z)) *\n"
"			SchlickDistribution_SchlickG(roughness, fabs(sampledDir.z));\n"
"}\n"
"\n"
"float GetPhi(const float a, const float b) {\n"
"	return M_PI_F * .5f * sqrt(a * b / (1.f - a * (1.f - b)));\n"
"}\n"
"\n"
"void SchlickDistribution_SampleH(const float roughness, const float anisotropy,\n"
"		const float u0, const float u1, float3 *wh, float *d, float *pdf) {\n"
"	float u1x4 = u1 * 4.f;\n"
"	// Values of roughness < .0001f seems to trigger some kind of exceptions with\n"
"	// AMD OpenCL on GPUs. The result is a nearly freeze of the PC.\n"
"	const float cos2Theta = (roughness < .0001f) ? 1.f : (u0 / (roughness * (1.f - u0) + u0));\n"
"	const float cosTheta = sqrt(cos2Theta);\n"
"	const float sinTheta = sqrt(1.f - cos2Theta);\n"
"	const float p = 1.f - fabs(anisotropy);\n"
"	float phi;\n"
"	if (u1x4 < 1.f) {\n"
"		phi = GetPhi(u1x4 * u1x4, p * p);\n"
"	} else if (u1x4 < 2.f) {\n"
"		u1x4 = 2.f - u1x4;\n"
"		phi = M_PI_F - GetPhi(u1x4 * u1x4, p * p);\n"
"	} else if (u1x4 < 3.f) {\n"
"		u1x4 -= 2.f;\n"
"		phi = M_PI_F + GetPhi(u1x4 * u1x4, p * p);\n"
"	} else {\n"
"		u1x4 = 4.f - u1x4;\n"
"		phi = M_PI_F * 2.f - GetPhi(u1x4 * u1x4, p * p);\n"
"	}\n"
"\n"
"	if (anisotropy > 0.f)\n"
"		phi += M_PI_F * .5f;\n"
"\n"
"	*wh = (float3)(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);\n"
"	*d = SchlickDistribution_SchlickZ(roughness, cosTheta) * SchlickDistribution_SchlickA(*wh, anisotropy) * M_1_PI_F;\n"
"	*pdf = *d;\n"
"}\n"
"\n"
"float SchlickDistribution_Pdf(const float roughness, const float3 wh,\n"
"		const float anisotropy) {\n"
"	return SchlickDistribution_D(roughness, wh, anisotropy);\n"
"}\n"
"\n"
"float3 FresnelSlick_Evaluate(const float3 normalIncidence, const float cosi) {\n"
"	return normalIncidence + (WHITE - normalIncidence) *\n"
"		pow(1.f - cosi, 5.f);\n"
"}\n"
"\n"
"float3 FrDiel2(const float cosi, const float3 cost, const float3 eta) {\n"
"	float3 Rparl = eta * cosi;\n"
"	Rparl = (cost - Rparl) / (cost + Rparl);\n"
"	float3 Rperp = eta * cost;\n"
"	Rperp = (cosi - Rperp) / (cosi + Rperp);\n"
"\n"
"	return (Rparl * Rparl + Rperp * Rperp) * .5f;\n"
"}\n"
"\n"
"float3 FrFull(const float cosi, const float3 cost, const float3 eta, const float3 k) {\n"
"	const float3 tmp = (eta * eta + k * k) * (cosi * cosi) + (cost * cost);\n"
"	const float3 Rparl2 = (tmp - (2.f * cosi * cost) * eta) /\n"
"		(tmp + (2.f * cosi * cost) * eta);\n"
"	const float3 tmp_f = (eta * eta + k * k) * (cost * cost) + (cosi * cosi);\n"
"	const float3 Rperp2 = (tmp_f - (2.f * cosi * cost) * eta) /\n"
"		(tmp_f + (2.f * cosi * cost) * eta);\n"
"	return (Rparl2 + Rperp2) * 0.5f;\n"
"}\n"
"\n"
"float3 FresnelGeneral_Evaluate(const float3 eta, const float3 k, const float cosi) {\n"
"	float3 sint2 = fmax(0.f, 1.f - cosi * cosi);\n"
"	if (cosi > 0.f)\n"
"		sint2 /= eta * eta;\n"
"	else\n"
"		sint2 *= eta * eta;\n"
"	sint2 = Spectrum_Clamp(sint2);\n"
"\n"
"	const float3 cost2 = 1.f - sint2;\n"
"	if (cosi > 0.f) {\n"
"		const float3 a = 2.f * k * k * sint2;\n"
"		return FrFull(cosi, Spectrum_Sqrt((cost2 + Spectrum_Sqrt(cost2 * cost2 + a * a)) / 2.f), eta, k);\n"
"	} else {\n"
"		const float3 a = 2.f * k * k * sint2;\n"
"		const float3 d2 = eta * eta + k * k;\n"
"		return FrFull(-cosi, Spectrum_Sqrt((cost2 + Spectrum_Sqrt(cost2 * cost2 + a * a)) / 2.f), eta / d2, -k / d2);\n"
"	}\n"
"}\n"
"\n"
"float3 FresnelCauchy_Evaluate(const float eta, const float cosi) {\n"
"	// Compute indices of refraction for dielectric\n"
"	const bool entering = (cosi > 0.f);\n"
"\n"
"	// Compute _sint_ using Snell's law\n"
"	const float eta2 = eta * eta;\n"
"	const float sint2 = (entering ? 1.f / eta2 : eta2) *\n"
"		fmax(0.f, 1.f - cosi * cosi);\n"
"	// Handle total internal reflection\n"
"	if (sint2 >= 1.f)\n"
"		return WHITE;\n"
"	else\n"
"		return FrDiel2(fabs(cosi), sqrt(fmax(0.f, 1.f - sint2)),\n"
"			entering ? eta : 1.f / eta);\n"
"}\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Matte material\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_MATTE)\n"
"\n"
"float3 MatteMaterial_Evaluate(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 lightDir, const float3 eyeDir,\n"
"		BSDFEvent *event, float *directPdfW\n"
"		TEXTURES_PARAM_DECL) {\n"
"	if (directPdfW)\n"
"		*directPdfW = fabs(lightDir.z * M_1_PI_F);\n"
"\n"
"	*event = DIFFUSE | REFLECT;\n"
"\n"
"	const float3 kd = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->matte.kdTexIndex], hitPoint\n"
"			TEXTURES_PARAM));\n"
"	return M_1_PI_F * kd;\n"
"}\n"
"\n"
"float3 MatteMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1, \n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	if (fabs(fixedDir.z) < DEFAULT_COS_EPSILON_STATIC)\n"
"		return BLACK;\n"
"\n"
"	*sampledDir = (signbit(fixedDir.z) ? -1.f : 1.f) * CosineSampleHemisphereWithPdf(u0, u1, pdfW);\n"
"\n"
"	*cosSampledDir = fabs((*sampledDir).z);\n"
"	if (*cosSampledDir < DEFAULT_COS_EPSILON_STATIC)\n"
"		return BLACK;\n"
"\n"
"	*event = DIFFUSE | REFLECT;\n"
"\n"
"	const float3 kd = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->matte.kdTexIndex], hitPoint\n"
"			TEXTURES_PARAM));\n"
"	return M_1_PI_F * kd;\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Mirror material\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_MIRROR)\n"
"\n"
"float3 MirrorMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1,\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	*event = SPECULAR | REFLECT;\n"
"\n"
"	*sampledDir = (float3)(-fixedDir.x, -fixedDir.y, fixedDir.z);\n"
"	*pdfW = 1.f;\n"
"\n"
"	*cosSampledDir = fabs((*sampledDir).z);\n"
"	const float3 kr = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->mirror.krTexIndex], hitPoint\n"
"			TEXTURES_PARAM));\n"
"	// The cosSampledDir is used to compensate the other one used inside the integrator\n"
"	return kr / (*cosSampledDir);\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Glass material\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_GLASS)\n"
"\n"
"float3 GlassMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 localFixedDir, float3 *localSampledDir,\n"
"		const float u0, const float u1, const float passThroughEvent,\n"
"		float *pdfW, float *absCosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const float3 kt = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glass.ktTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"	const float3 kr = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glass.krTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"\n"
"	const bool isKtBlack = Spectrum_IsBlack(kt);\n"
"	const bool isKrBlack = Spectrum_IsBlack(kr);\n"
"	if (isKtBlack && isKrBlack)\n"
"		return BLACK;\n"
"\n"
"	const bool entering = (CosTheta(localFixedDir) > 0.f);\n"
"	const float nc = Texture_GetFloatValue(&texs[material->glass.ousideIorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float nt = Texture_GetFloatValue(&texs[material->glass.iorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float ntc = nt / nc;\n"
"	const float eta = entering ? (nc / nt) : ntc;\n"
"	const float costheta = CosTheta(localFixedDir);\n"
"\n"
"	// Decide to transmit or reflect\n"
"	const float threshold = isKrBlack ? 1.f : (isKtBlack ? 0.f : .5f);\n"
"	float3 result;\n"
"	if (passThroughEvent < threshold) {\n"
"		// Transmit\n"
"	\n"
"		// Compute transmitted ray direction\n"
"		const float sini2 = SinTheta2(localFixedDir);\n"
"		const float eta2 = eta * eta;\n"
"		const float sint2 = eta2 * sini2;\n"
"\n"
"		// Handle total internal reflection for transmission\n"
"		if (sint2 >= 1.f)\n"
"			return BLACK;\n"
"\n"
"		const float cost = sqrt(fmax(0.f, 1.f - sint2)) * (entering ? -1.f : 1.f);\n"
"		*localSampledDir = (float3)(-eta * localFixedDir.x, -eta * localFixedDir.y, cost);\n"
"		*absCosSampledDir = fabs(CosTheta(*localSampledDir));\n"
"\n"
"		*event = SPECULAR | TRANSMIT;\n"
"		*pdfW = threshold;\n"
"\n"
"		//if (!hitPoint.fromLight)\n"
"			result = (1.f - FresnelCauchy_Evaluate(ntc, cost)) * eta2;\n"
"		//else\n"
"		//	result = (1.f - FresnelCauchy_Evaluate(ntc, costheta));\n"
"\n"
"		result *= kt;\n"
"	} else {\n"
"		// Reflect\n"
"		*localSampledDir = (float3)(-localFixedDir.x, -localFixedDir.y, localFixedDir.z);\n"
"		*absCosSampledDir = fabs(CosTheta(*localSampledDir));\n"
"\n"
"		*event = SPECULAR | REFLECT;\n"
"		*pdfW = 1.f - threshold;\n"
"\n"
"		result = kr * FresnelCauchy_Evaluate(ntc, costheta);\n"
"	}\n"
"\n"
"	// The absCosSampledDir is used to compensate the other one used inside the integrator\n"
"	return result / (*absCosSampledDir);\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Metal material\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_METAL)\n"
"\n"
"float3 GlossyReflection(const float3 fixedDir, const float exponent,\n"
"		const float u0, const float u1) {\n"
"	// Ray from outside going in ?\n"
"	const bool into = (fixedDir.z > 0.f);\n"
"	const float3 shadeN = (float3)(0.f, 0.f, into ? 1.f : -1.f);\n"
"\n"
"	const float phi = 2.f * M_PI_F * u0;\n"
"	const float cosTheta = pow(1.f - u1, exponent);\n"
"	const float sinTheta = sqrt(fmax(0.f, 1.f - cosTheta * cosTheta));\n"
"	const float x = cos(phi) * sinTheta;\n"
"	const float y = sin(phi) * sinTheta;\n"
"	const float z = cosTheta;\n"
"\n"
"	const float3 dir = -fixedDir;\n"
"	const float dp = dot(shadeN, dir);\n"
"	const float3 w = dir - (2.f * dp) * shadeN;\n"
"\n"
"	const float3 u = normalize(cross(\n"
"			(fabs(shadeN.x) > .1f) ? ((float3)(0.f, 1.f, 0.f)) : ((float3)(1.f, 0.f, 0.f)),\n"
"			w));\n"
"	const float3 v = cross(w, u);\n"
"\n"
"	return x * u + y * v + z * w;\n"
"}\n"
"\n"
"float3 MetalMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1,\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const float e = 1.f / (Texture_GetFloatValue(&texs[material->metal.expTexIndex], hitPoint\n"
"				TEXTURES_PARAM) + 1.f);\n"
"	*sampledDir = GlossyReflection(fixedDir, e, u0, u1);\n"
"\n"
"	if ((*sampledDir).z * fixedDir.z > 0.f) {\n"
"		*event = SPECULAR | REFLECT;\n"
"		*pdfW = 1.f;\n"
"		*cosSampledDir = fabs((*sampledDir).z);\n"
"\n"
"		const float3 kt = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->metal.krTexIndex], hitPoint\n"
"				TEXTURES_PARAM));\n"
"		// The cosSampledDir is used to compensate the other one used inside the integrator\n"
"		return kt / (*cosSampledDir);\n"
"	} else\n"
"		return BLACK;\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// ArchGlass material\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_ARCHGLASS)\n"
"\n"
"float3 ArchGlassMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 localFixedDir, float3 *localSampledDir,\n"
"		const float u0, const float u1, const float passThroughEvent,\n"
"		float *pdfW, float *absCosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const float3 kt = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glass.ktTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"	const float3 kr = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glass.krTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"\n"
"	const bool isKtBlack = Spectrum_IsBlack(kt);\n"
"	const bool isKrBlack = Spectrum_IsBlack(kr);\n"
"	if (isKtBlack && isKrBlack)\n"
"		return BLACK;\n"
"\n"
"	const bool entering = (CosTheta(localFixedDir) > 0.f);\n"
"	const float nc = Texture_GetFloatValue(&texs[material->glass.ousideIorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float nt = Texture_GetFloatValue(&texs[material->glass.iorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float ntc = nt / nc;\n"
"	const float eta = nc / nt;\n"
"	const float costheta = CosTheta(localFixedDir);\n"
"\n"
"	// Decide to transmit or reflect\n"
"	const float threshold = isKrBlack ? 1.f : (isKtBlack ? 0.f : .5f);\n"
"	float3 result;\n"
"	if (passThroughEvent < threshold) {\n"
"		// Transmit\n"
"\n"
"		// Compute transmitted ray direction\n"
"		const float sini2 = SinTheta2(localFixedDir);\n"
"		const float eta2 = eta * eta;\n"
"		const float sint2 = eta2 * sini2;\n"
"\n"
"		// Handle total internal reflection for transmission\n"
"		if (sint2 >= 1.f)\n"
"			return BLACK;\n"
"\n"
"		*localSampledDir = -localFixedDir;\n"
"		*absCosSampledDir = fabs(CosTheta(*localSampledDir));\n"
"\n"
"		*event = SPECULAR | TRANSMIT;\n"
"		*pdfW = threshold;\n"
"\n"
"		//if (!hitPoint.fromLight) {\n"
"			if (entering)\n"
"				result = BLACK;\n"
"			else\n"
"				result = FresnelCauchy_Evaluate(ntc, -costheta);\n"
"		//} else {\n"
"		//	if (entering)\n"
"		//		result = FresnelCauchy_Evaluate(ntc, costheta);\n"
"		//	else\n"
"		//		result = BLACK;\n"
"		//}\n"
"		result *= 1.f + (1.f - result) * (1.f - result);\n"
"		result = 1.f - result;\n"
"\n"
"		result *= kt;\n"
"	} else {\n"
"		// Reflect\n"
"		if (costheta <= 0.f)\n"
"			return BLACK;\n"
"\n"
"		*localSampledDir = (float3)(-localFixedDir.x, -localFixedDir.y, localFixedDir.z);\n"
"		*absCosSampledDir = fabs(CosTheta(*localSampledDir));\n"
"\n"
"		*event = SPECULAR | REFLECT;\n"
"		*pdfW = 1.f - threshold;\n"
"\n"
"		result = kr * FresnelCauchy_Evaluate(ntc, costheta);\n"
"	}\n"
"\n"
"	// The absCosSampledDir is used to compensate the other one used inside the integrator\n"
"	return result / (*absCosSampledDir);\n"
"}\n"
"\n"
"float3 ArchGlassMaterial_GetPassThroughTransparency(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 localFixedDir, const float passThroughEvent\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const float3 kt = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glass.ktTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"	const float3 kr = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glass.krTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"\n"
"	const bool isKtBlack = Spectrum_IsBlack(kt);\n"
"	const bool isKrBlack = Spectrum_IsBlack(kr);\n"
"	if (isKtBlack && isKrBlack)\n"
"		return BLACK;\n"
"\n"
"	const bool entering = (CosTheta(localFixedDir) > 0.f);\n"
"	const float nc = Texture_GetFloatValue(&texs[material->glass.ousideIorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float nt = Texture_GetFloatValue(&texs[material->glass.iorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float ntc = nt / nc;\n"
"	const float eta = nc / nt;\n"
"	const float costheta = CosTheta(localFixedDir);\n"
"\n"
"	// Decide to transmit or reflect\n"
"	const float threshold = isKrBlack ? 1.f : (isKtBlack ? 0.f : .5f);\n"
"	if (passThroughEvent < threshold) {\n"
"		// Transmit\n"
"\n"
"		// Compute transmitted ray direction\n"
"		const float sini2 = SinTheta2(localFixedDir);\n"
"		const float eta2 = eta * eta;\n"
"		const float sint2 = eta2 * sini2;\n"
"\n"
"		// Handle total internal reflection for transmission\n"
"		if (sint2 >= 1.f)\n"
"			return BLACK;\n"
"\n"
"		float3 result;\n"
"		//if (!hitPoint.fromLight) {\n"
"			if (entering)\n"
"				result = BLACK;\n"
"			else\n"
"				result = FresnelCauchy_Evaluate(ntc, -costheta);\n"
"		//} else {\n"
"		//	if (entering)\n"
"		//		result = FresnelCauchy_Evaluate(ntc, costheta);\n"
"		//	else\n"
"		//		result = Spectrum();\n"
"		//}\n"
"		result *= 1.f + (1.f - result) * (1.f - result);\n"
"		result = 1.f - result;\n"
"\n"
"		return kt * result;\n"
"	} else\n"
"		return BLACK;\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// NULL material\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_NULL)\n"
"\n"
"float3 NullMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1,\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	*sampledDir = -fixedDir;\n"
"	*cosSampledDir = 1.f;\n"
"\n"
"	*pdfW = 1.f;\n"
"	*event = SPECULAR | TRANSMIT;\n"
"	return WHITE;\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// MatteTranslucent material\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_MATTETRANSLUCENT)\n"
"\n"
"float3 MatteTranslucentMaterial_Evaluate(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 lightDir, const float3 eyeDir,\n"
"		BSDFEvent *event, float *directPdfW\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const float cosSampledDir = dot(lightDir, eyeDir);\n"
"\n"
"	const float3 r = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->matteTranslucent.krTexIndex], hitPoint\n"
"			TEXTURES_PARAM));\n"
"	const float3 t = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->matteTranslucent.ktTexIndex], hitPoint\n"
"			TEXTURES_PARAM)) * \n"
"		// Energy conservation\n"
"		(1.f - r);\n"
"\n"
"	if (directPdfW)\n"
"		*directPdfW = .5f * fabs(lightDir.z * M_1_PI_F);\n"
"\n"
"	if (cosSampledDir > 0.f) {\n"
"		*event = DIFFUSE | REFLECT;\n"
"		return r * M_1_PI_F;\n"
"	} else {\n"
"		*event = DIFFUSE | TRANSMIT;\n"
"		return t * M_1_PI_F;\n"
"	}\n"
"}\n"
"\n"
"float3 MatteTranslucentMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1, \n"
"		const float passThroughEvent,\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	if (fabs(fixedDir.z) < DEFAULT_COS_EPSILON_STATIC)\n"
"		return BLACK;\n"
"\n"
"	*sampledDir = CosineSampleHemisphereWithPdf(u0, u1, pdfW);\n"
"	*cosSampledDir = fabs((*sampledDir).z);\n"
"	if (*cosSampledDir < DEFAULT_COS_EPSILON_STATIC)\n"
"		return BLACK;\n"
"\n"
"	*pdfW *= .5f;\n"
"\n"
"	const float3 r = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->matteTranslucent.krTexIndex], hitPoint\n"
"			TEXTURES_PARAM));\n"
"	const float3 t = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->matteTranslucent.ktTexIndex], hitPoint\n"
"			TEXTURES_PARAM)) * \n"
"		// Energy conservation\n"
"		(1.f - r);\n"
"\n"
"	if (passThroughEvent < .5f) {\n"
"		*sampledDir *= (signbit(fixedDir.z) ? -1.f : 1.f);\n"
"		*event = DIFFUSE | REFLECT;\n"
"		return r * M_1_PI_F;\n"
"	} else {\n"
"		*sampledDir *= -(signbit(fixedDir.z) ? -1.f : 1.f);\n"
"		*event = DIFFUSE | TRANSMIT;\n"
"		return t * M_1_PI_F;\n"
"	}\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Glossy2 material\n"
"//\n"
"// LuxRender Glossy2 material porting.\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_GLOSSY2)\n"
"\n"
"float SchlickBSDF_CoatingWeight(const float3 ks, const float3 fixedDir) {\n"
"	// No sampling on the back face\n"
"	if (fixedDir.z <= 0.f)\n"
"		return 0.f;\n"
"\n"
"	// Approximate H by using reflection direction for wi\n"
"	const float u = fabs(fixedDir.z);\n"
"	const float3 S = FresnelSlick_Evaluate(ks, u);\n"
"\n"
"	// Ensures coating is never sampled less than half the time\n"
"	// unless we are on the back face\n"
"	return .5f * (1.f + Spectrum_Filter(S));\n"
"}\n"
"\n"
"float3 SchlickBSDF_CoatingF(const float3 ks, const float roughness,\n"
"		const float anisotropy, const int multibounce, const float3 fixedDir,\n"
"		const float3 sampledDir) {\n"
"	// No sampling on the back face\n"
"	if (fixedDir.z <= 0.f)\n"
"		return BLACK;\n"
"\n"
"	const float coso = fabs(fixedDir.z);\n"
"	const float cosi = fabs(sampledDir.z);\n"
"\n"
"	const float3 wh = normalize(fixedDir + sampledDir);\n"
"	const float3 S = FresnelSlick_Evaluate(ks, fabs(dot(sampledDir, wh)));\n"
"\n"
"	const float G = SchlickDistribution_G(roughness, fixedDir, sampledDir);\n"
"\n"
"	// Multibounce - alternative with interreflection in the coating creases\n"
"	float factor = SchlickDistribution_D(roughness, wh, anisotropy) * G;\n"
"	//if (!fromLight)\n"
"		factor = factor / 4.f * coso +\n"
"				(multibounce ? cosi * clamp((1.f - G) / (4.f * coso * cosi), 0.f, 1.f) : 0.f);\n"
"	//else\n"
"	//	factor = factor / (4.f * cosi) + \n"
"	//			(multibounce ? coso * Clamp((1.f - G) / (4.f * cosi * coso), 0.f, 1.f) : 0.f);\n"
"\n"
"	// The cosi is used to compensate the other one used inside the integrator\n"
"	factor /= cosi;\n"
"	return factor * S;\n"
"}\n"
"\n"
"float3 SchlickBSDF_CoatingAbsorption(const float cosi, const float coso,\n"
"		const float3 alpha, const float depth) {\n"
"	if (depth > 0.f) {\n"
"		// 1/cosi+1/coso=(cosi+coso)/(cosi*coso)\n"
"		const float depthFactor = depth * (cosi + coso) / (cosi * coso);\n"
"		return Spectrum_Exp(alpha * -depthFactor);\n"
"	} else\n"
"		return WHITE;\n"
"}\n"
"\n"
"float3 SchlickBSDF_CoatingSampleF(const float3 ks,\n"
"		const float roughness, const float anisotropy, const int multibounce,\n"
"		const float3 fixedDir, float3 *sampledDir,\n"
"		float u0, float u1, float *pdf) {\n"
"	// No sampling on the back face\n"
"	if (fixedDir.z <= 0.f)\n"
"		return BLACK;\n"
"\n"
"	float3 wh;\n"
"	float d, specPdf;\n"
"	SchlickDistribution_SampleH(roughness, anisotropy, u0, u1, &wh, &d, &specPdf);\n"
"	const float cosWH = dot(fixedDir, wh);\n"
"	*sampledDir = 2.f * cosWH * wh - fixedDir;\n"
"\n"
"	if (((*sampledDir).z < DEFAULT_COS_EPSILON_STATIC) || (fixedDir.z * (*sampledDir).z < 0.f))\n"
"		return BLACK;\n"
"\n"
"	const float coso = fabs(fixedDir.z);\n"
"	const float cosi = fabs((*sampledDir).z);\n"
"\n"
"	*pdf = specPdf / (4.f * cosWH);\n"
"	if (*pdf <= 0.f)\n"
"		return BLACK;\n"
"\n"
"	float3 S = FresnelSlick_Evaluate(ks, cosWH);\n"
"\n"
"	const float G = SchlickDistribution_G(roughness, fixedDir, *sampledDir);\n"
"\n"
"	//CoatingF(sw, *wi, wo, f_);\n"
"	S *= d * G / (4.f * coso) + \n"
"			(multibounce ? cosi * clamp((1.f - G) / (4.f * coso * cosi), 0.f, 1.f) : 0.f);\n"
"\n"
"	// The cosi is used to compensate the other one used inside the integrator\n"
"	return S / cosi;\n"
"}\n"
"\n"
"float SchlickBSDF_CoatingPdf(const float roughness, const float anisotropy,\n"
"		const float3 fixedDir, const float3 sampledDir) {\n"
"	// No sampling on the back face\n"
"	if (fixedDir.z <= 0.f)\n"
"		return 0.f;\n"
"\n"
"	const float3 wh = normalize(fixedDir + sampledDir);\n"
"	return SchlickDistribution_Pdf(roughness, wh, anisotropy) / (4.f * fabs(dot(fixedDir, wh)));\n"
"}\n"
"\n"
"float3 Glossy2Material_Evaluate(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 lightDir, const float3 eyeDir,\n"
"		BSDFEvent *event, float *directPdfW\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const float3 fixedDir = eyeDir;\n"
"	const float3 sampledDir = lightDir;\n"
"\n"
"	const float3 baseF = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glossy2.kdTexIndex], hitPoint\n"
"			TEXTURES_PARAM)) * M_1_PI_F;\n"
"	if (eyeDir.z <= 0.f) {\n"
"		// Back face: no coating\n"
"\n"
"		if (directPdfW)\n"
"			*directPdfW = fabs(sampledDir.z * M_1_PI_F);\n"
"\n"
"		*event = DIFFUSE | REFLECT;\n"
"		return baseF;\n"
"	}\n"
"\n"
"	// Front face: coating+base\n"
"	*event = GLOSSY | REFLECT;\n"
"\n"
"	float3 ks = Texture_GetSpectrumValue(&texs[material->glossy2.ksTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"#if defined(PARAM_ENABLE_MAT_GLOSSY2_INDEX)\n"
"	const float i = Texture_GetFloatValue(&texs[material->glossy2.indexTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	if (i > 0.f) {\n"
"		const float ti = (i - 1.f) / (i + 1.f);\n"
"		ks *= ti * ti;\n"
"	}\n"
"#endif\n"
"	ks = Spectrum_Clamp(ks);\n"
"\n"
"	const float u = clamp(Texture_GetFloatValue(&texs[material->glossy2.nuTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"#if defined(PARAM_ENABLE_MAT_GLOSSY2_ANISOTROPIC)\n"
"	const float v = clamp(Texture_GetFloatValue(&texs[material->glossy2.nvTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"	const float u2 = u * u;\n"
"	const float v2 = v * v;\n"
"	const float anisotropy = (u2 < v2) ? (1.f - u2 / v2) : (v2 / u2 - 1.f);\n"
"	const float roughness = u * v;\n"
"#else\n"
"	const float anisotropy = 0.f;\n"
"	const float roughness = u * u;\n"
"#endif\n"
"\n"
"	if (directPdfW) {\n"
"		const float wCoating = SchlickBSDF_CoatingWeight(ks, fixedDir);\n"
"		const float wBase = 1.f - wCoating;\n"
"\n"
"		*directPdfW = wBase * fabs(sampledDir.z * M_1_PI_F) +\n"
"				wCoating * SchlickBSDF_CoatingPdf(roughness, anisotropy, fixedDir, sampledDir);\n"
"	}\n"
"\n"
"	// Absorption\n"
"	const float cosi = fabs(sampledDir.z);\n"
"	const float coso = fabs(fixedDir.z);\n"
"\n"
"#if defined(PARAM_ENABLE_MAT_GLOSSY2_ANISOTROPIC)\n"
"	const float3 alpha = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glossy2.kaTexIndex], hitPoint\n"
"			TEXTURES_PARAM));\n"
"	const float d = Texture_GetFloatValue(&texs[material->glossy2.depthTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float3 absorption = SchlickBSDF_CoatingAbsorption(cosi, coso, alpha, d);\n"
"#else\n"
"	const float3 absorption = WHITE;\n"
"#endif\n"
"\n"
"	// Coating fresnel factor\n"
"	const float3 H = normalize(fixedDir + sampledDir);\n"
"	const float3 S = FresnelSlick_Evaluate(ks, fabs(dot(sampledDir, H)));\n"
"\n"
"#if defined(PARAM_ENABLE_MAT_GLOSSY2_MULTIBOUNCE)\n"
"	const int multibounce = material->glossy2.multibounce;\n"
"#else\n"
"	const int multibounce = 0;\n"
"#endif\n"
"	const float3 coatingF = SchlickBSDF_CoatingF(ks, roughness, anisotropy, multibounce,\n"
"			fixedDir, sampledDir);\n"
"\n"
"	// Blend in base layer Schlick style\n"
"	// assumes coating bxdf takes fresnel factor S into account\n"
"\n"
"	return coatingF + absorption * (WHITE - S) * baseF;\n"
"}\n"
"\n"
"float3 Glossy2Material_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1, \n"
"		const float passThroughEvent,\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	if (fabs(fixedDir.z) < DEFAULT_COS_EPSILON_STATIC)\n"
"		return BLACK;\n"
"\n"
"	float3 ks = Texture_GetSpectrumValue(&texs[material->glossy2.ksTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"#if defined(PARAM_ENABLE_MAT_GLOSSY2_INDEX)\n"
"	const float i = Texture_GetFloatValue(&texs[material->glossy2.indexTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	if (i > 0.f) {\n"
"		const float ti = (i - 1.f) / (i + 1.f);\n"
"		ks *= ti * ti;\n"
"	}\n"
"#endif\n"
"	ks = Spectrum_Clamp(ks);\n"
"\n"
"	const float u = clamp(Texture_GetFloatValue(&texs[material->glossy2.nuTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"#if defined(PARAM_ENABLE_MAT_GLOSSY2_ANISOTROPIC)\n"
"	const float v = clamp(Texture_GetFloatValue(&texs[material->glossy2.nvTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"	const float u2 = u * u;\n"
"	const float v2 = v * v;\n"
"	const float anisotropy = (u2 < v2) ? (1.f - u2 / v2) : (v2 / u2 - 1.f);\n"
"	const float roughness = u * v;\n"
"#else\n"
"	const float anisotropy = 0.f;\n"
"	const float roughness = u * u;\n"
"#endif\n"
"\n"
"	// Coating is used only on the front face\n"
"	const float wCoating = (fixedDir.z <= 0.f) ? 0.f : SchlickBSDF_CoatingWeight(ks, fixedDir);\n"
"	const float wBase = 1.f - wCoating;\n"
"\n"
"	const float3 baseF = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glossy2.kdTexIndex], hitPoint\n"
"		TEXTURES_PARAM)) * M_1_PI_F;\n"
"\n"
"#if defined(PARAM_ENABLE_MAT_GLOSSY2_MULTIBOUNCE)\n"
"	const int multibounce = material->glossy2.multibounce;\n"
"#else\n"
"	const int multibounce = 0;\n"
"#endif\n"
"\n"
"	float basePdf, coatingPdf;\n"
"	float3 coatingF;\n"
"	if (passThroughEvent < wBase) {\n"
"		// Sample base BSDF (Matte BSDF)\n"
"		*sampledDir = (signbit(fixedDir.z) ? -1.f : 1.f) * CosineSampleHemisphereWithPdf(u0, u1, &basePdf);\n"
"\n"
"		*cosSampledDir = fabs((*sampledDir).z);\n"
"		if (*cosSampledDir < DEFAULT_COS_EPSILON_STATIC)\n"
"			return BLACK;\n"
"\n"
"		// Evaluate coating BSDF (Schlick BSDF)\n"
"		coatingF = SchlickBSDF_CoatingF(ks, roughness, anisotropy, multibounce,\n"
"				fixedDir, *sampledDir);\n"
"		coatingPdf = SchlickBSDF_CoatingPdf(roughness, anisotropy, fixedDir, *sampledDir);\n"
"\n"
"		*event = DIFFUSE | REFLECT;\n"
"	} else {\n"
"		// Sample coating BSDF (Schlick BSDF)\n"
"		coatingF = SchlickBSDF_CoatingSampleF(ks, roughness, anisotropy,\n"
"				multibounce, fixedDir, sampledDir, u0, u1, &coatingPdf);\n"
"		if (Spectrum_IsBlack(coatingF))\n"
"			return BLACK;\n"
"\n"
"		*cosSampledDir = fabs((*sampledDir).z);\n"
"		if (*cosSampledDir < DEFAULT_COS_EPSILON_STATIC)\n"
"			return BLACK;\n"
"\n"
"		// Evaluate base BSDF (Matte BSDF)\n"
"		basePdf = fabs((*sampledDir).z * M_1_PI_F);\n"
"\n"
"		*event = GLOSSY | REFLECT;\n"
"	}\n"
"\n"
"	*pdfW = coatingPdf * wCoating + basePdf * wBase;\n"
"	if (fixedDir.z > 0.f) {\n"
"		// Front face reflection: coating+base\n"
"\n"
"		// Absorption\n"
"		const float cosi = fabs((*sampledDir).z);\n"
"		const float coso = fabs(fixedDir.z);\n"
"\n"
"#if defined(PARAM_ENABLE_MAT_GLOSSY2_ANISOTROPIC)\n"
"		const float3 alpha = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->glossy2.kaTexIndex], hitPoint\n"
"			TEXTURES_PARAM));\n"
"		const float d = Texture_GetFloatValue(&texs[material->glossy2.depthTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"		const float3 absorption = SchlickBSDF_CoatingAbsorption(cosi, coso, alpha, d);\n"
"#else\n"
"		const float3 absorption = WHITE;\n"
"#endif\n"
"\n"
"		// Coating fresnel factor\n"
"		const float3 H = normalize(fixedDir + *sampledDir);\n"
"		const float3 S = FresnelSlick_Evaluate(ks, fabs(dot(*sampledDir, H)));\n"
"\n"
"		// Blend in base layer Schlick style\n"
"		// coatingF already takes fresnel factor S into account\n"
"\n"
"		return coatingF + absorption * (WHITE - S) * baseF;\n"
"	} else {\n"
"		// Back face reflection: base\n"
"\n"
"		return baseF;\n"
"	}\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Metal2 material\n"
"//\n"
"// LuxRender Metal2 material porting.\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_METAL2)\n"
"\n"
"float3 Metal2Material_Evaluate(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 lightDir, const float3 eyeDir,\n"
"		BSDFEvent *event, float *directPdfW\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const float3 fixedDir = eyeDir;\n"
"	const float3 sampledDir = lightDir;\n"
"\n"
"	const float u = clamp(Texture_GetFloatValue(&texs[material->metal2.nuTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"#if defined(PARAM_ENABLE_MAT_METAL2_ANISOTROPIC)\n"
"	const float v = clamp(Texture_GetFloatValue(&texs[material->metal2.nvTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"	const float u2 = u * u;\n"
"	const float v2 = v * v;\n"
"	const float anisotropy = (u2 < v2) ? (1.f - u2 / v2) : (v2 / u2 - 1.f);\n"
"	const float roughness = u * v;\n"
"#else\n"
"	const float anisotropy = 0.f;\n"
"	const float roughness = u * u;\n"
"#endif\n"
"\n"
"	const float3 wh = normalize(fixedDir + sampledDir);\n"
"	const float cosWH = dot(fixedDir, wh);\n"
"\n"
"	if (directPdfW)\n"
"		*directPdfW = SchlickDistribution_Pdf(roughness, wh, anisotropy) / (4.f * fabs(dot(fixedDir, wh)));\n"
"\n"
"	const float3 nVal = Texture_GetSpectrumValue(&texs[material->metal2.nTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float3 kVal = Texture_GetSpectrumValue(&texs[material->metal2.kTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"\n"
"	const float3 F = FresnelGeneral_Evaluate(nVal, kVal, cosWH);\n"
"\n"
"	const float G = SchlickDistribution_G(roughness, fixedDir, sampledDir);\n"
"\n"
"	const float coso = fabs(fixedDir.z);\n"
"	const float cosi = fabs(sampledDir.z);\n"
"	float factor = SchlickDistribution_D(roughness, wh, anisotropy) * G;\n"
"	//if (!hitPoint.fromLight)\n"
"		factor /= 4.f * coso;\n"
"	//else\n"
"	//	factor /= 4.f * cosi;\n"
"\n"
"	*event = GLOSSY | REFLECT;\n"
"\n"
"	// The cosi is used to compensate the other one used inside the integrator\n"
"	factor /= cosi;\n"
"	return factor * F;\n"
"}\n"
"\n"
"float3 Metal2Material_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1,\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	if (fabs(fixedDir.z) < DEFAULT_COS_EPSILON_STATIC)\n"
"		return BLACK;\n"
"\n"
"	const float u = clamp(Texture_GetFloatValue(&texs[material->metal2.nuTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"#if defined(PARAM_ENABLE_MAT_METAL2_ANISOTROPIC)\n"
"	const float v = clamp(Texture_GetFloatValue(&texs[material->metal2.nvTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"	const float u2 = u * u;\n"
"	const float v2 = v * v;\n"
"	const float anisotropy = (u2 < v2) ? (1.f - u2 / v2) : (v2 / u2 - 1.f);\n"
"	const float roughness = u * v;\n"
"#else\n"
"	const float anisotropy = 0.f;\n"
"	const float roughness = u * u;\n"
"#endif\n"
"\n"
"	float3 wh;\n"
"	float d, specPdf;\n"
"	SchlickDistribution_SampleH(roughness, anisotropy, u0, u1, &wh, &d, &specPdf);\n"
"	const float cosWH = dot(fixedDir, wh);\n"
"	*sampledDir = 2.f * cosWH * wh - fixedDir;\n"
"\n"
"	const float coso = fabs(fixedDir.z);\n"
"	const float cosi = fabs((*sampledDir).z);\n"
"	*cosSampledDir = cosi;\n"
"	if ((*cosSampledDir < DEFAULT_COS_EPSILON_STATIC) || (fixedDir.z * (*sampledDir).z < 0.f))\n"
"		return BLACK;\n"
"\n"
"	*pdfW = specPdf / (4.f * cosWH);\n"
"	if (*pdfW <= 0.f)\n"
"		return BLACK;\n"
"\n"
"	const float G = SchlickDistribution_G(roughness, fixedDir, *sampledDir);\n"
"	\n"
"	const float3 nVal = Texture_GetSpectrumValue(&texs[material->metal2.nTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float3 kVal = Texture_GetSpectrumValue(&texs[material->metal2.kTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float3 F = FresnelGeneral_Evaluate(nVal, kVal, cosWH);\n"
"\n"
"	float factor = d * G;\n"
"	//if (!fromLight)\n"
"		factor /= 4.f * coso;\n"
"	//else\n"
"	//	factor /= cosi;\n"
"\n"
"	*event = GLOSSY | REFLECT;\n"
"\n"
"	// The cosi is used to compensate the other one used inside the integrator\n"
"	factor /= cosi;\n"
"	return factor * F;\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// RoughGlass material\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_ROUGHGLASS)\n"
"\n"
"float3 RoughGlassMaterial_Evaluate(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 localLightDir, const float3 localEyeDir,\n"
"		BSDFEvent *event, float *directPdfW\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const float3 kt = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->roughglass.ktTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"	const float3 kr = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->roughglass.krTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"\n"
"	const bool isKtBlack = Spectrum_IsBlack(kt);\n"
"	const bool isKrBlack = Spectrum_IsBlack(kr);\n"
"	if (isKtBlack && isKrBlack)\n"
"		return BLACK;\n"
"	\n"
"	//const float3 localFixedDir = hitPoint.fromLight ? localLightDir : localEyeDir;\n"
"	//const float3 localSampledDir = hitPoint.fromLight ? localEyeDir : localLightDir;\n"
"	const float3 localFixedDir = localEyeDir;\n"
"	const float3 localSampledDir = localLightDir;\n"
"\n"
"	const float nc = Texture_GetFloatValue(&texs[material->roughglass.ousideIorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float nt = Texture_GetFloatValue(&texs[material->roughglass.iorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float ntc = nt / nc;\n"
"\n"
"	const float u = clamp(Texture_GetFloatValue(&texs[material->roughglass.nuTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"#if defined(PARAM_ENABLE_MAT_ROUGHGLASS_ANISOTROPIC)\n"
"	const float v = clamp(Texture_GetFloatValue(&texs[material->roughglass.nvTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"	const float u2 = u * u;\n"
"	const float v2 = v * v;\n"
"	const float anisotropy = (u2 < v2) ? (1.f - u2 / v2) : (v2 / u2 - 1.f);\n"
"	const float roughness = u * v;\n"
"#else\n"
"	const float anisotropy = 0.f;\n"
"	const float roughness = u * u;\n"
"#endif\n"
"\n"
"	const float threshold = isKrBlack ? 1.f : (isKtBlack ? 0.f : .5f);\n"
"	if (dot(localFixedDir, localSampledDir) < 0.f) {\n"
"		// Transmit\n"
"\n"
"		const bool entering = (CosTheta(localFixedDir) > 0.f);\n"
"		const float eta = entering ? (nc / nt) : ntc;\n"
"\n"
"		float3 wh = eta * localFixedDir + localSampledDir;\n"
"		if (wh.z < 0.f)\n"
"			wh = -wh;\n"
"\n"
"		//const float lengthSquared = wh.LengthSquared();\n"
"		const float lengthSquared = dot(wh, wh);\n"
"		if (!(lengthSquared > 0.f))\n"
"			return BLACK;\n"
"		wh /= sqrt(lengthSquared);\n"
"		const float cosThetaI = fabs(CosTheta(localSampledDir));\n"
"		const float cosThetaIH = fabs(dot(localSampledDir, wh));\n"
"		const float cosThetaOH = dot(localFixedDir, wh);\n"
"\n"
"		const float D = SchlickDistribution_D(roughness, wh, anisotropy);\n"
"		const float G = SchlickDistribution_G(roughness, localFixedDir, localSampledDir);\n"
"		const float specPdf = SchlickDistribution_Pdf(roughness, wh, anisotropy);\n"
"		const float3 F = FresnelCauchy_Evaluate(ntc, cosThetaOH);\n"
"\n"
"		if (directPdfW)\n"
"			*directPdfW = threshold * specPdf * fabs(cosThetaOH) / lengthSquared;\n"
"\n"
"		//if (reversePdfW)\n"
"		//	*reversePdfW = threshold * specPdf * cosThetaIH * eta * eta / lengthSquared;\n"
"\n"
"		float3 result = (fabs(cosThetaOH) * cosThetaIH * D *\n"
"			G / (cosThetaI * lengthSquared)) *\n"
"			kt * (1.f - F);\n"
"\n"
"		// This is a porting of LuxRender code and there, the result is multiplied\n"
"		// by Dot(ns, wl). So I have to remove that term.\n"
"		result /= fabs(CosTheta(localLightDir));\n"
"		return result;\n"
"	} else {\n"
"		// Reflect\n"
"		const float cosThetaO = fabs(CosTheta(localFixedDir));\n"
"		const float cosThetaI = fabs(CosTheta(localSampledDir));\n"
"		if (cosThetaO == 0.f || cosThetaI == 0.f)\n"
"			return BLACK;\n"
"		float3 wh = localFixedDir + localSampledDir;\n"
"		if (all(isequal(wh, BLACK)))\n"
"			return BLACK;\n"
"		wh = normalize(wh);\n"
"		if (wh.z < 0.f)\n"
"			wh = -wh;\n"
"\n"
"		float cosThetaH = dot(localEyeDir, wh);\n"
"		const float D = SchlickDistribution_D(roughness, wh, anisotropy);\n"
"		const float G = SchlickDistribution_G(roughness, localFixedDir, localSampledDir);\n"
"		const float specPdf = SchlickDistribution_Pdf(roughness, wh, anisotropy);\n"
"		const float3 F = FresnelCauchy_Evaluate(ntc, cosThetaH);\n"
"\n"
"		if (directPdfW)\n"
"			*directPdfW = (1.f - threshold) * specPdf / (4.f * fabs(dot(localFixedDir, wh)));\n"
"\n"
"		//if (reversePdfW)\n"
"		//	*reversePdfW = (1.f - threshold) * specPdf / (4.f * fabs(dot(localSampledDir, wh));\n"
"\n"
"		float3 result = (D * G / (4.f * cosThetaI)) * kr * F;\n"
"\n"
"		// This is a porting of LuxRender code and there, the result is multiplied\n"
"		// by Dot(ns, wl). So I have to remove that term.\n"
"		result /= fabs(CosTheta(localLightDir));\n"
"		return result;\n"
"	}\n"
"}\n"
"\n"
"float3 RoughGlassMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 localFixedDir, float3 *localSampledDir,\n"
"		const float u0, const float u1, const float passThroughEvent,\n"
"		float *pdfW, float *absCosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	if (fabs(localFixedDir.z) < DEFAULT_COS_EPSILON_STATIC)\n"
"		return BLACK;\n"
"\n"
"	const float3 kt = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->roughglass.ktTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"	const float3 kr = Spectrum_Clamp(Texture_GetSpectrumValue(&texs[material->roughglass.krTexIndex], hitPoint\n"
"		TEXTURES_PARAM));\n"
"\n"
"	const bool isKtBlack = Spectrum_IsBlack(kt);\n"
"	const bool isKrBlack = Spectrum_IsBlack(kr);\n"
"	if (isKtBlack && isKrBlack)\n"
"		return BLACK;\n"
"\n"
"	const float u = clamp(Texture_GetFloatValue(&texs[material->roughglass.nuTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"#if defined(PARAM_ENABLE_MAT_ROUGHGLASS_ANISOTROPIC)\n"
"	const float v = clamp(Texture_GetFloatValue(&texs[material->roughglass.nvTexIndex], hitPoint\n"
"		TEXTURES_PARAM), 6e-3f, 1.f);\n"
"	const float u2 = u * u;\n"
"	const float v2 = v * v;\n"
"	const float anisotropy = (u2 < v2) ? (1.f - u2 / v2) : (v2 / u2 - 1.f);\n"
"	const float roughness = u * v;\n"
"#else\n"
"	const float anisotropy = 0.f;\n"
"	const float roughness = u * u;\n"
"#endif\n"
"\n"
"	float3 wh;\n"
"	float d, specPdf;\n"
"	SchlickDistribution_SampleH(roughness, anisotropy, u0, u1, &wh, &d, &specPdf);\n"
"	if (wh.z < 0.f)\n"
"		wh = -wh;\n"
"	const float cosThetaOH = dot(localFixedDir, wh);\n"
"\n"
"	const float nc = Texture_GetFloatValue(&texs[material->roughglass.ousideIorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float nt = Texture_GetFloatValue(&texs[material->roughglass.iorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"	const float ntc = nt / nc;\n"
"\n"
"	const float coso = fabs(localFixedDir.z);\n"
"\n"
"	// Decide to transmit or reflect\n"
"	const float threshold = isKrBlack ? 1.f : (isKtBlack ? 0.f : .5f);\n"
"	float3 result;\n"
"	if (passThroughEvent < threshold) {\n"
"		// Transmit\n"
"\n"
"		const bool entering = (CosTheta(localFixedDir) > 0.f);\n"
"		const float eta = entering ? (nc / nt) : ntc;\n"
"		const float eta2 = eta * eta;\n"
"		const float sinThetaIH2 = eta2 * fmax(0.f, 1.f - cosThetaOH * cosThetaOH);\n"
"		if (sinThetaIH2 >= 1.f)\n"
"			return BLACK;\n"
"		float cosThetaIH = sqrt(1.f - sinThetaIH2);\n"
"		if (entering)\n"
"			cosThetaIH = -cosThetaIH;\n"
"		const float length = eta * cosThetaOH + cosThetaIH;\n"
"		*localSampledDir = length * wh - eta * localFixedDir;\n"
"\n"
"		const float lengthSquared = length * length;\n"
"		*pdfW = specPdf * fabs(cosThetaIH) / lengthSquared;\n"
"		if (*pdfW <= 0.f)\n"
"			return BLACK;\n"
"\n"
"		const float cosi = fabs((*localSampledDir).z);\n"
"		*absCosSampledDir = cosi;\n"
"\n"
"		const float G = SchlickDistribution_G(roughness, localFixedDir, *localSampledDir);\n"
"		float factor = d * G * fabs(cosThetaOH) / specPdf;\n"
"\n"
"		//if (!hitPoint.fromLight) {\n"
"			const float3 F = FresnelCauchy_Evaluate(ntc, cosThetaIH);\n"
"			result = (factor / coso) * kt * (1.f - F);\n"
"		//} else {\n"
"		//	const Spectrum F = FresnelCauchy_Evaluate(ntc, cosThetaOH);\n"
"		//	result = (factor / cosi) * kt * (Spectrum(1.f) - F);\n"
"		//}\n"
"\n"
"		// This is a porting of LuxRender code and there, the result is multiplied\n"
"		// by Dot(ns, wi)/pdf if reverse==true and by Dot(ns. wo)/pdf if reverse==false.\n"
"		// So I have to remove that terms.\n"
"		//result *= *pdfW / ((!hitPoint.fromLight) ? cosi : coso);\n"
"			result *= *pdfW / cosi;\n"
"		*pdfW *= threshold;\n"
"		*event = GLOSSY | TRANSMIT;\n"
"	} else {\n"
"		// Reflect\n"
"		*pdfW = specPdf / (4.f * fabs(cosThetaOH));\n"
"		if (*pdfW <= 0.f)\n"
"			return BLACK;\n"
"\n"
"		*localSampledDir = 2.f * cosThetaOH * wh - localFixedDir;\n"
"\n"
"		const float cosi = fabs((*localSampledDir).z);\n"
"		*absCosSampledDir = cosi;\n"
"		if ((*absCosSampledDir < DEFAULT_COS_EPSILON_STATIC) || (localFixedDir.z * (*localSampledDir).z < 0.f))\n"
"			return BLACK;\n"
"\n"
"		const float G = SchlickDistribution_G(roughness, localFixedDir, *localSampledDir);\n"
"		float factor = d * G * fabs(cosThetaOH) / specPdf;\n"
"\n"
"		const float3 F = FresnelCauchy_Evaluate(ntc, cosThetaOH);\n"
"		//factor /= (!hitPoint.fromLight) ? coso : cosi;\n"
"		factor /= coso;\n"
"		result = factor * F * kr;\n"
"\n"
"		// This is a porting of LuxRender code and there, the result is multiplied\n"
"		// by Dot(ns, wi)/pdf if reverse==true and by Dot(ns. wo)/pdf if reverse==false.\n"
"		// So I have to remove that terms.\n"
"		//result *= *pdfW / ((!hitPoint.fromLight) ? cosi : coso);\n"
"		result *= *pdfW / cosi;\n"
"		*pdfW *= (1.f - threshold);\n"
"		*event = GLOSSY | REFLECT;\n"
"	}\n"
"\n"
"	return result;\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Generic material functions\n"
"//\n"
"// They include the support for all material but Mix\n"
"// (because OpenCL doesn't support recursion)\n"
"//------------------------------------------------------------------------------\n"
"\n"
"bool Material_IsDeltaNoMix(__global Material *material) {\n"
"	switch (material->type) {\n"
"		// Non Specular materials\n"
"#if defined (PARAM_ENABLE_MAT_ROUGHGLASS)\n"
"		case ROUGHGLASS:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_METAL2)\n"
"		case METAL2:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_GLOSSY2)\n"
"		case GLOSSY2:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_MATTETRANSLUCENT)\n"
"		case MATTETRANSLUCENT:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_MATTE)\n"
"		case MATTE:\n"
"			return false;\n"
"#endif\n"
"		// Specular materials\n"
"#if defined (PARAM_ENABLE_MAT_MIRROR)\n"
"		case MIRROR:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_GLASS)\n"
"		case GLASS:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_METAL)\n"
"		case METAL:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_ARCHGLASS)\n"
"		case ARCHGLASS:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_NULL)\n"
"		case NULLMAT:\n"
"#endif\n"
"		default:\n"
"			return true;\n"
"	}\n"
"}\n"
"\n"
"BSDFEvent Material_GetEventTypesNoMix(__global Material *mat) {\n"
"	switch (mat->type) {\n"
"#if defined (PARAM_ENABLE_MAT_MATTE)\n"
"		case MATTE:\n"
"			return DIFFUSE | REFLECT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_MIRROR)\n"
"		case MIRROR:\n"
"			return SPECULAR | REFLECT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_GLASS)\n"
"		case GLASS:\n"
"			return SPECULAR | REFLECT | TRANSMIT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_METAL)\n"
"		case METAL:\n"
"			return SPECULAR | REFLECT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_ARCHGLASS)\n"
"		case ARCHGLASS:\n"
"			return SPECULAR | REFLECT | TRANSMIT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_NULL)\n"
"		case NULLMAT:\n"
"			return SPECULAR | TRANSMIT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_MATTETRANSLUCENT)\n"
"		case MATTETRANSLUCENT:\n"
"			return DIFFUSE | REFLECT | TRANSMIT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_GLOSSY2)\n"
"		case GLOSSY2:\n"
"			return DIFFUSE | GLOSSY | REFLECT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_METAL2)\n"
"		case METAL2:\n"
"			return GLOSSY | REFLECT;\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_ROUGHGLASS)\n"
"		case ROUGHGLASS:\n"
"			return GLOSSY | REFLECT | TRANSMIT;\n"
"#endif\n"
"		default:\n"
"			return NONE;\n"
"	}\n"
"}\n"
"\n"
"float3 Material_SampleNoMix(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1,\n"
"#if defined(PARAM_HAS_PASSTHROUGH)\n"
"		const float passThroughEvent,\n"
"#endif\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		TEXTURES_PARAM_DECL) {\n"
"	switch (material->type) {\n"
"#if defined (PARAM_ENABLE_MAT_MATTE)\n"
"		case MATTE:\n"
"			return MatteMaterial_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1,	pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_MIRROR)\n"
"		case MIRROR:\n"
"			return MirrorMaterial_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1, pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_GLASS)\n"
"		case GLASS:\n"
"			return GlassMaterial_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1,	passThroughEvent, pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_METAL)\n"
"		case METAL:\n"
"			return MetalMaterial_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1,	pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_ARCHGLASS)\n"
"		case ARCHGLASS:\n"
"			return ArchGlassMaterial_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1,	passThroughEvent, pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_NULL)\n"
"		case NULLMAT:\n"
"			return NullMaterial_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1, pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_MATTETRANSLUCENT)\n"
"		case MATTETRANSLUCENT:\n"
"			return MatteTranslucentMaterial_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1,	passThroughEvent, pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_GLOSSY2)\n"
"		case GLOSSY2:\n"
"			return Glossy2Material_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1,	passThroughEvent, pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_METAL2)\n"
"		case METAL2:\n"
"			return Metal2Material_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1,	pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_ROUGHGLASS)\n"
"		case ROUGHGLASS:\n"
"			return RoughGlassMaterial_Sample(material, hitPoint, fixedDir, sampledDir,\n"
"					u0, u1,	passThroughEvent, pdfW, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"		default:\n"
"			return BLACK;\n"
"	}\n"
"}\n"
"\n"
"float3 Material_EvaluateNoMix(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 lightDir, const float3 eyeDir,\n"
"		BSDFEvent *event, float *directPdfW\n"
"		TEXTURES_PARAM_DECL) {\n"
"	switch (material->type) {\n"
"#if defined (PARAM_ENABLE_MAT_MATTE)\n"
"		case MATTE:\n"
"			return MatteMaterial_Evaluate(material, hitPoint, lightDir, eyeDir, event, directPdfW\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_MATTETRANSLUCENT)\n"
"		case MATTETRANSLUCENT:\n"
"			return MatteTranslucentMaterial_Evaluate(material, hitPoint, lightDir, eyeDir, event, directPdfW\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_GLOSSY2)\n"
"		case GLOSSY2:\n"
"			return Glossy2Material_Evaluate(material, hitPoint, lightDir, eyeDir, event, directPdfW\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_METAL2)\n"
"		case METAL2:\n"
"			return Metal2Material_Evaluate(material, hitPoint, lightDir, eyeDir, event, directPdfW\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_ROUGHGLASS)\n"
"		case ROUGHGLASS:\n"
"			return RoughGlassMaterial_Evaluate(material, hitPoint, lightDir, eyeDir, event, directPdfW\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_MIRROR)\n"
"		case MIRROR:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_GLASS)\n"
"		case GLASS:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_METAL)\n"
"		case METAL:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_ARCHGLASS)\n"
"		case ARCHGLASS:\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_NULL)\n"
"		case NULLMAT:\n"
"#endif\n"
"		default:\n"
"			return BLACK;\n"
"	}\n"
"}\n"
"\n"
"float3 Material_GetEmittedRadianceNoMix(__global Material *material, __global HitPoint *hitPoint\n"
"		TEXTURES_PARAM_DECL) {\n"
"	const uint emitTexIndex = material->emitTexIndex;\n"
"	if (emitTexIndex == NULL_INDEX)\n"
"		return BLACK;\n"
"\n"
"	return Texture_GetSpectrumValue(&texs[emitTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"}\n"
"\n"
"float3 Material_GetPassThroughTransparencyNoMix(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, const float passThroughEvent\n"
"		TEXTURES_PARAM_DECL) {\n"
"	switch (material->type) {\n"
"#if defined (PARAM_ENABLE_MAT_ARCHGLASS)\n"
"		case ARCHGLASS:\n"
"			return ArchGlassMaterial_GetPassThroughTransparency(material,\n"
"					hitPoint, fixedDir, passThroughEvent\n"
"					TEXTURES_PARAM);\n"
"#endif\n"
"#if defined (PARAM_ENABLE_MAT_NULL)\n"
"		case NULLMAT:\n"
"			return WHITE;\n"
"#endif\n"
"		default:\n"
"			return BLACK;\n"
"	}\n"
"}\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Mix material\n"
"//\n"
"// This requires a quite complex implementation because OpenCL doesn't support\n"
"// recursion.\n"
"//------------------------------------------------------------------------------\n"
"\n"
"#if defined (PARAM_ENABLE_MAT_MIX)\n"
"\n"
"#define MIX_STACK_SIZE 16\n"
"\n"
"BSDFEvent MixMaterial_IsDelta(__global Material *material\n"
"		MATERIALS_PARAM_DECL) {\n"
"	__global Material *materialStack[MIX_STACK_SIZE];\n"
"	materialStack[0] = material;\n"
"	int stackIndex = 0;\n"
"\n"
"	while (stackIndex >= 0) {\n"
"		// Extract a material from the stack\n"
"		__global Material *m = materialStack[stackIndex--];\n"
"\n"
"		// Check if it is a Mix material too\n"
"		if (m->type == MIX) {\n"
"			// Add both material to the stack\n"
"			materialStack[++stackIndex] = &mats[m->mix.matAIndex];\n"
"			materialStack[++stackIndex] = &mats[m->mix.matBIndex];\n"
"		} else {\n"
"			// Normal GetEventTypes() evaluation\n"
"			if (!Material_IsDeltaNoMix(m))\n"
"				return false;\n"
"		}\n"
"	}\n"
"\n"
"	return true;\n"
"}\n"
"\n"
"BSDFEvent MixMaterial_GetEventTypes(__global Material *material\n"
"		MATERIALS_PARAM_DECL) {\n"
"	BSDFEvent event = NONE;\n"
"	__global Material *materialStack[MIX_STACK_SIZE];\n"
"	materialStack[0] = material;\n"
"	int stackIndex = 0;\n"
"\n"
"	while (stackIndex >= 0) {\n"
"		// Extract a material from the stack\n"
"		__global Material *m = materialStack[stackIndex--];\n"
"\n"
"		// Check if it is a Mix material too\n"
"		if (m->type == MIX) {\n"
"			// Add both material to the stack\n"
"			materialStack[++stackIndex] = &mats[m->mix.matAIndex];\n"
"			materialStack[++stackIndex] = &mats[m->mix.matBIndex];\n"
"		} else {\n"
"			// Normal GetEventTypes() evaluation\n"
"			event |= Material_GetEventTypesNoMix(m);\n"
"		}\n"
"	}\n"
"\n"
"	return event;\n"
"}\n"
"\n"
"float3 MixMaterial_Evaluate(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 lightDir, const float3 eyeDir,\n"
"		BSDFEvent *event, float *directPdfW\n"
"		MATERIALS_PARAM_DECL) {\n"
"	__global Material *materialStack[MIX_STACK_SIZE];\n"
"	float totalWeightStack[MIX_STACK_SIZE];\n"
"\n"
"	// Push the root Mix material\n"
"	materialStack[0] = material;\n"
"	totalWeightStack[0] = 1.f;\n"
"	int stackIndex = 0;\n"
"\n"
"	// Setup the results\n"
"	float3 result = BLACK;\n"
"	*event = NONE;\n"
"	if (directPdfW)\n"
"		*directPdfW = 0.f;\n"
"\n"
"	while (stackIndex >= 0) {\n"
"		// Extract a material from the stack\n"
"		__global Material *m = materialStack[stackIndex];\n"
"		float totalWeight = totalWeightStack[stackIndex--];\n"
"\n"
"		// Check if it is a Mix material too\n"
"		if (m->type == MIX) {\n"
"			// Add both material to the stack\n"
"			const float factor = Texture_GetFloatValue(&texs[m->mix.mixFactorTexIndex], hitPoint\n"
"					TEXTURES_PARAM);\n"
"			const float weight2 = clamp(factor, 0.f, 1.f);\n"
"			const float weight1 = 1.f - weight2;\n"
"\n"
"			materialStack[++stackIndex] = &mats[m->mix.matAIndex];\n"
"			totalWeightStack[stackIndex] = totalWeight * weight1;\n"
"\n"
"			materialStack[++stackIndex] = &mats[m->mix.matBIndex];			\n"
"			totalWeightStack[stackIndex] = totalWeight * weight2;\n"
"		} else {\n"
"			// Normal Evaluate() evaluation\n"
"			if (totalWeight > 0.f) {\n"
"				BSDFEvent eventMat;\n"
"				float directPdfWMat;\n"
"				const float3 resultMat = Material_EvaluateNoMix(m, hitPoint, lightDir, eyeDir, &eventMat, &directPdfWMat\n"
"						TEXTURES_PARAM);\n"
"\n"
"				if (!Spectrum_IsBlack(resultMat)) {\n"
"					*event |= eventMat;\n"
"					result += totalWeight * resultMat;\n"
"\n"
"					if (directPdfW)\n"
"						*directPdfW += totalWeight * directPdfWMat;\n"
"				}\n"
"			}\n"
"		}\n"
"	}\n"
"\n"
"	return result;\n"
"}\n"
"\n"
"float3 MixMaterial_Sample(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1, const float passEvent,\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		MATERIALS_PARAM_DECL) {\n"
"	__global Material *evaluationMatList[MIX_STACK_SIZE];\n"
"	float parentWeightList[MIX_STACK_SIZE];\n"
"	int evaluationListSize = 0;\n"
"\n"
"	// Setup the results\n"
"	float3 result = BLACK;\n"
"	*pdfW = 0.f;\n"
"\n"
"	// Look for a no Mix material to sample\n"
"	__global Material *currentMixMat = material;\n"
"	float passThroughEvent = passEvent;\n"
"	float parentWeight = 1.f;\n"
"	for (;;) {\n"
"		const float factor = Texture_GetFloatValue(&texs[currentMixMat->mix.mixFactorTexIndex], hitPoint\n"
"			TEXTURES_PARAM);\n"
"		const float weight2 = clamp(factor, 0.f, 1.f);\n"
"		const float weight1 = 1.f - weight2;\n"
"\n"
"		const bool sampleMatA = (passThroughEvent < weight1);\n"
"\n"
"		const float weightFirst = sampleMatA ? weight1 : weight2;\n"
"		const float weightSecond = sampleMatA ? weight2 : weight1;\n"
"\n"
"		const float passThroughEventFirst = sampleMatA ? (passThroughEvent / weight1) : (passThroughEvent - weight1) / weight2;\n"
"\n"
"		const uint matIndexFirst = sampleMatA ? currentMixMat->mix.matAIndex : currentMixMat->mix.matBIndex;\n"
"		const uint matIndexSecond = sampleMatA ? currentMixMat->mix.matBIndex : currentMixMat->mix.matAIndex;\n"
"\n"
"		// Sample the first material, evaluate the second\n"
"		__global Material *matFirst = &mats[matIndexFirst];\n"
"		__global Material *matSecond = &mats[matIndexSecond];\n"
"\n"
"		//----------------------------------------------------------------------\n"
"		// Add the second material to the evaluation list\n"
"		//----------------------------------------------------------------------\n"
"\n"
"		if (weightSecond > 0.f) {\n"
"			evaluationMatList[evaluationListSize] = matSecond;\n"
"			parentWeightList[evaluationListSize++] = parentWeight * weightSecond;\n"
"		}\n"
"\n"
"		//----------------------------------------------------------------------\n"
"		// Sample the first material\n"
"		//----------------------------------------------------------------------\n"
"\n"
"		// Check if it is a Mix material too\n"
"		if (matFirst->type == MIX) {\n"
"			// Make the first material the current\n"
"			currentMixMat = matFirst;\n"
"			passThroughEvent = passThroughEventFirst;\n"
"			parentWeight *= weightFirst;\n"
"		} else {\n"
"			// Sample the first material\n"
"			float pdfWMatFirst;\n"
"			const float3 sampleResult = Material_SampleNoMix(matFirst, hitPoint,\n"
"					fixedDir, sampledDir,\n"
"					u0, u1, passThroughEventFirst,\n"
"					&pdfWMatFirst, cosSampledDir, event\n"
"					TEXTURES_PARAM);\n"
"\n"
"			if (all(isequal(sampleResult, BLACK)))\n"
"				return BLACK;\n"
"\n"
"			const float weight = parentWeight * weightFirst;\n"
"			*pdfW += weight * pdfWMatFirst;\n"
"			result += weight * sampleResult;\n"
"\n"
"			// I can stop now\n"
"			break;\n"
"		}\n"
"	}\n"
"\n"
"	while (evaluationListSize > 0) {\n"
"		// Extract the material to evaluate\n"
"		__global Material *evalMat = evaluationMatList[--evaluationListSize];\n"
"		const float evalWeight = parentWeightList[evaluationListSize];\n"
"\n"
"		// Evaluate the material\n"
"\n"
"		// Check if it is a Mix material too\n"
"		BSDFEvent eventMat;\n"
"		float pdfWMat;\n"
"		float3 eval;\n"
"		if (evalMat->type == MIX) {\n"
"			eval = MixMaterial_Evaluate(evalMat, hitPoint, *sampledDir, fixedDir,\n"
"					&eventMat, &pdfWMat\n"
"					MATERIALS_PARAM);\n"
"		} else {\n"
"			eval = Material_EvaluateNoMix(evalMat, hitPoint, *sampledDir, fixedDir,\n"
"					&eventMat, &pdfWMat\n"
"					TEXTURES_PARAM);\n"
"		}\n"
"		if (!Spectrum_IsBlack(eval)) {\n"
"			result += evalWeight * eval;\n"
"			*pdfW += evalWeight * pdfWMat;\n"
"		}\n"
"	}\n"
"\n"
"	return result;\n"
"}\n"
"\n"
"float3 MixMaterial_GetEmittedRadiance(__global Material *material, __global HitPoint *hitPoint\n"
"		MATERIALS_PARAM_DECL) {\n"
"	__global Material *materialStack[MIX_STACK_SIZE];\n"
"	float totalWeightStack[MIX_STACK_SIZE];\n"
"\n"
"	// Push the root Mix material\n"
"	materialStack[0] = material;\n"
"	totalWeightStack[0] = 1.f;\n"
"	int stackIndex = 0;\n"
"\n"
"	// Setup the results\n"
"	float3 result = BLACK;\n"
"\n"
"	while (stackIndex >= 0) {\n"
"		// Extract a material from the stack\n"
"		__global Material *m = materialStack[stackIndex];\n"
"		float totalWeight = totalWeightStack[stackIndex--];\n"
"\n"
"		if (m->type == MIX) {\n"
"			const float factor = Texture_GetFloatValue(&texs[m->mix.mixFactorTexIndex], hitPoint\n"
"					TEXTURES_PARAM);\n"
"			const float weight2 = clamp(factor, 0.f, 1.f);\n"
"			const float weight1 = 1.f - weight2;\n"
"\n"
"			if (weight1 > 0.f) {\n"
"				materialStack[++stackIndex] = &mats[m->mix.matAIndex];\n"
"				totalWeightStack[stackIndex] = totalWeight * weight1;\n"
"			}\n"
"\n"
"			if (weight2 > 0.f) {\n"
"				materialStack[++stackIndex] = &mats[m->mix.matBIndex];\n"
"				totalWeightStack[stackIndex] = totalWeight * weight2;\n"
"			}\n"
"		} else {\n"
"			const float3 emitRad = Material_GetEmittedRadianceNoMix(m, hitPoint\n"
"				TEXTURES_PARAM);\n"
"			if (!Spectrum_IsBlack(emitRad))\n"
"				result += totalWeight * emitRad;\n"
"		}\n"
"	}\n"
"	\n"
"	return result;\n"
"}\n"
"\n"
"float3 MixMaterial_GetPassThroughTransparency(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, const float passEvent\n"
"		MATERIALS_PARAM_DECL) {\n"
"	__global Material *currentMixMat = material;\n"
"	float passThroughEvent = passEvent;\n"
"	for (;;) {\n"
"		const float factor = Texture_GetFloatValue(&texs[currentMixMat->mix.mixFactorTexIndex], hitPoint\n"
"				TEXTURES_PARAM);\n"
"		const float weight2 = clamp(factor, 0.f, 1.f);\n"
"		const float weight1 = 1.f - weight2;\n"
"\n"
"		const bool sampleMatA = (passThroughEvent < weight1);\n"
"		passThroughEvent = sampleMatA ? (passThroughEvent / weight1) : (passThroughEvent - weight1) / weight2;\n"
"		const uint matIndex = sampleMatA ? currentMixMat->mix.matAIndex : currentMixMat->mix.matBIndex;\n"
"		__global Material *mat = &mats[matIndex];\n"
"\n"
"		if (mat->type == MIX) {\n"
"			currentMixMat = mat;\n"
"			continue;\n"
"		} else\n"
"			return Material_GetPassThroughTransparencyNoMix(mat, hitPoint, fixedDir, passThroughEvent\n"
"					TEXTURES_PARAM);\n"
"	}\n"
"}\n"
"\n"
"#endif\n"
"\n"
"//------------------------------------------------------------------------------\n"
"// Generic material functions with Mix support\n"
"//------------------------------------------------------------------------------\n"
"\n"
"BSDFEvent Material_GetEventTypes(__global Material *material\n"
"		MATERIALS_PARAM_DECL) {\n"
"#if defined (PARAM_ENABLE_MAT_MIX)\n"
"	if (material->type == MIX)\n"
"		return MixMaterial_GetEventTypes(material\n"
"				MATERIALS_PARAM);\n"
"	else\n"
"#endif\n"
"		return Material_GetEventTypesNoMix(material);\n"
"}\n"
"\n"
"bool Material_IsDelta(__global Material *material\n"
"		MATERIALS_PARAM_DECL) {\n"
"#if defined (PARAM_ENABLE_MAT_MIX)\n"
"	if (material->type == MIX)\n"
"		return MixMaterial_IsDelta(material\n"
"				MATERIALS_PARAM);\n"
"	else\n"
"#endif\n"
"		return Material_IsDeltaNoMix(material);\n"
"}\n"
"\n"
"float3 Material_Evaluate(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 lightDir, const float3 eyeDir,\n"
"		BSDFEvent *event, float *directPdfW\n"
"		MATERIALS_PARAM_DECL) {\n"
"#if defined (PARAM_ENABLE_MAT_MIX)\n"
"	if (material->type == MIX)\n"
"		return MixMaterial_Evaluate(material, hitPoint, lightDir, eyeDir,\n"
"				event, directPdfW\n"
"				MATERIALS_PARAM);\n"
"	else\n"
"#endif\n"
"		return Material_EvaluateNoMix(material, hitPoint, lightDir, eyeDir,\n"
"				event, directPdfW\n"
"				TEXTURES_PARAM);\n"
"}\n"
"\n"
"float3 Material_Sample(__global Material *material,	__global HitPoint *hitPoint,\n"
"		const float3 fixedDir, float3 *sampledDir,\n"
"		const float u0, const float u1,\n"
"#if defined(PARAM_HAS_PASSTHROUGH)\n"
"		const float passThroughEvent,\n"
"#endif\n"
"		float *pdfW, float *cosSampledDir, BSDFEvent *event\n"
"		MATERIALS_PARAM_DECL) {\n"
"#if defined (PARAM_ENABLE_MAT_MIX)\n"
"	if (material->type == MIX)\n"
"		return MixMaterial_Sample(material, hitPoint,\n"
"				fixedDir, sampledDir,\n"
"				u0, u1,\n"
"				passThroughEvent,\n"
"				pdfW, cosSampledDir, event\n"
"				MATERIALS_PARAM);\n"
"	else\n"
"#endif\n"
"		return Material_SampleNoMix(material, hitPoint,\n"
"				fixedDir, sampledDir,\n"
"				u0, u1,\n"
"#if defined(PARAM_HAS_PASSTHROUGH)\n"
"				passThroughEvent,\n"
"#endif\n"
"				pdfW, cosSampledDir, event\n"
"				TEXTURES_PARAM);\n"
"}\n"
"\n"
"float3 Material_GetEmittedRadiance(__global Material *material, __global HitPoint *hitPoint\n"
"		MATERIALS_PARAM_DECL) {\n"
"#if defined (PARAM_ENABLE_MAT_MIX)\n"
"	if (material->type == MIX)\n"
"		return MixMaterial_GetEmittedRadiance(material, hitPoint\n"
"				MATERIALS_PARAM);\n"
"	else\n"
"#endif\n"
"		return Material_GetEmittedRadianceNoMix(material, hitPoint\n"
"				TEXTURES_PARAM);\n"
"}\n"
"\n"
"#if defined(PARAM_HAS_PASSTHROUGH)\n"
"float3 Material_GetPassThroughTransparency(__global Material *material,\n"
"		__global HitPoint *hitPoint, const float3 fixedDir, const float passThroughEvent\n"
"		MATERIALS_PARAM_DECL) {\n"
"#if defined (PARAM_ENABLE_MAT_MIX)\n"
"	if (material->type == MIX)\n"
"		return MixMaterial_GetPassThroughTransparency(material,\n"
"				hitPoint, fixedDir, passThroughEvent\n"
"				MATERIALS_PARAM);\n"
"	else\n"
"#endif\n"
"		return Material_GetPassThroughTransparencyNoMix(material,\n"
"				hitPoint, fixedDir, passThroughEvent\n"
"				TEXTURES_PARAM);\n"
"}\n"
"#endif\n"
; } }
