#// STDLIB.MSL



#//
#//				Blinn MATERIAL
#//

#DEFINE_MATERIAL_PASS blinn transparency
material blinn(out fragment float4 outRGBA)
{
	outRGBA = float4(evaluate("transparency"), 1.0);
}

#DEFINE_MATERIAL_PASS blinn lighting
material blinn(out fragment float4 outRGBA)
{
	// Compute view-specific Blinn specular parameters.
	float3 worldViewDir = normalize(gWorldEyePos - gWorldPos);
    float3 worldReflectedViewDir = reflect(-worldViewDir, gWorldBumpedNormal);
	float eccentricity = evaluate("eccentricity");
	
	// Light-independent Blinn specular parameters
	float cosne = dot(gWorldBumpedNormal, worldViewDir);
	float computedEcc = eccentricity * eccentricity - 1.0;

	float3 ambientIrradiance  = float3(0.0,0.0,0.0);
	float3 diffuseIrradiance  = float3(0.0,0.0,0.0);
	float3 specularIrradiance = float3(0.0,0.0,0.0);
	illuminance
	{
		// Accumulate the different types of irradiance.
		ambientIrradiance += gLightAmbientColor;

		float cosln = max(dot(gWorldBumpedNormal, gLightWorldDir), 0.0);
		diffuseIrradiance += cosln * gLightDiffuseColor;

		// Accumulate the specular irradiance.
		//
		float3 wHalfAngleDir = normalize(gLightWorldDir + worldViewDir);

		float coseh = dot(wHalfAngleDir, worldViewDir);
		float cosnh = dot(wHalfAngleDir, gWorldBumpedNormal);
	
		float Dd = ( computedEcc + 1.0) / ( 1.0 + computedEcc * cosnh * cosnh );
		Dd    = Dd * Dd;
		cosnh = 2.0 * cosnh;

		float Gg;
		if (cosne < cosln) 
			Gg = (cosne*cosnh < coseh) ? cosnh / coseh : 1.0 / cosne;
		else 
			Gg = (cosln*cosnh < coseh) ? cosln * cosnh / (coseh * cosne) : 1.0 / cosne;

		// Fresnel calculation.
		//
		coseh = 1.0 - coseh;
		coseh = coseh * coseh * coseh;
		float Ff = coseh + (1.0 - coseh) * evaluate("specularRollOff");
		float specularCoefficient = max(Dd * Gg * Ff, 0.0);

		if (cosln > 0.0)
			specularIrradiance += specularCoefficient * gLightSpecularColor;
	}

	diffuseIrradiance = evaluate("diffuse") * diffuseIrradiance;
	float3 opacityColor = float3(1.0,1.0,1.0) - evaluate("transparency");
	float3 diffuseAndAmbientColor = 
		(diffuseIrradiance + ambientIrradiance) * evaluate("color") * opacityColor;

	float3 specularColor = specularIrradiance * evaluate("specularColor");

	outRGBA = float4(diffuseAndAmbientColor + specularColor, 1.0);
}

#DEFINE_MATERIAL_PASS blinn postLighting
material blinn(out fragment float4 outRGBA)
{
	// Compute view-specific Blinn specular parameters.
	float3 worldViewDir = normalize(gWorldEyePos - gWorldPos);
    float3 worldReflectedViewDir = reflect(-worldViewDir, gWorldBumpedNormal);

	float3 reflectedColor = evaluate(worldReflectedViewDir, "reflectedColor").rgb;
	reflectedColor = reflectedColor * evaluate("specularColor");

	// Compute the fresnel term used to modulate reflectivity.
	//
	float cosne = dot(gWorldBumpedNormal, worldViewDir);
	cosne = 1.0 - cosne;
	cosne = cosne * cosne * cosne;
	float reflectance = cosne + (1.0 - cosne);
	reflectance = reflectance * evaluate("specularRollOff");
	reflectance = max(reflectance, 0.0) * evaluate("reflectivity");

	float3 ambientColor = evaluate("ambientColor") * 
		evaluate("color") * (float3(1.0,1.0,1.0) - evaluate("transparency"));
	
	outRGBA = float4(reflectance * reflectedColor + ambientColor + evaluate("incandescence"), 1.0);
}

#DEFINE_MATERIAL_PASS blinn alpha
material blinn(out fragment float4 outRGBA)
{
	// TODO: Handle matte opacity, use background and other details.
	//
	float3 coloredOpacity = float3(1.0,1.0,1.0) - evaluate("transparency");
	outRGBA = float4(coloredOpacity, 1.0);
}



#//
#//				Lambert MATERIAL
#//

#DEFINE_MATERIAL_PASS lambert transparency
material lambert(out fragment float4 outRGBA)
{
	outRGBA = float4(evaluate("transparency"), 1.0);
}

#DEFINE_MATERIAL_PASS lambert lighting
material lambert(out fragment float4 outRGBA)
{
	float3 ambientIrradiance = float3(0.0,0.0,0.0);
	float3 diffuseIrradiance = float3(0.0,0.0,0.0);
	illuminance
	{
		// Accumulate the different types of irradiance.
		ambientIrradiance += gLightAmbientColor;

		float cosln = max(dot(gWorldBumpedNormal, gLightWorldDir), 0.0);
		diffuseIrradiance += (cosln * gLightDiffuseColor);
	}

	diffuseIrradiance = evaluate("diffuse") * diffuseIrradiance;
	float3 opacityColor = float3(1.0,1.0,1.0) - evaluate("transparency");
	float3 diffuseAndAmbientColor = 
		(diffuseIrradiance + ambientIrradiance) * evaluate("color") * opacityColor;

	outRGBA = float4(diffuseAndAmbientColor, 1.0);
}

#DEFINE_MATERIAL_PASS lambert postLighting
material lambert(out fragment float4 outRGBA)
{
	float3 opacityColor = float3(1.0,1.0,1.0) - evaluate("transparency");
	float3 ambientColor = evaluate("ambientColor") * evaluate("color") * opacityColor;
	outRGBA = float4(ambientColor + evaluate("incandescence"), 1.0);
}

#DEFINE_MATERIAL_PASS lambert alpha
material lambert(out fragment float4 outRGBA)
{
	// TODO: Handle matte opacity, use background and other details.
	//
	float3 coloredOpacity = float3(1.0,1.0,1.0) - evaluate("transparency");
	outRGBA = float4(coloredOpacity, 1.0);
}



#//
#//				SurfaceShader MATERIAL
#//

#DEFINE_MATERIAL_PASS surfaceShader transparency
material Maya_surfaceShader(out fragment float4 outRGBA)
{
	outRGBA = float4(evaluate("outTransparency"), 1.0);
}

#// SurfaceShader color pass is always black.

#DEFINE_MATERIAL_PASS surfaceShader postLighting
material Maya_surfaceShader(out fragment float4 outRGBA)
{
	outRGBA = float4(evaluate("outColor") * (float3(1.0,1.0,1.0)-evaluate("outTransparency")), 1.0);
}

#DEFINE_MATERIAL_PASS surfaceShader alpha

material Maya_surfaceShader(out fragment float4 outRGBA)
{
	// TODO: Handle matte opacity, use background and other details.
	//
	float3 coloredOpacity = float3(1.0,1.0,1.0) - evaluate("outTransparency");
	outRGBA = float4(coloredOpacity, 1.0);
}



#//
#//				Phong MATERIAL
#//

#DEFINE_MATERIAL_PASS phong transparency
material phong(out fragment float4 outRGBA)
{
	outRGBA = float4(evaluate("transparency"), 1.0);
}

#DEFINE_MATERIAL_PASS phong lighting
material phong(out fragment float4 outRGBA)
{
    // Calculate view direction in world space,
	// from point to eye.
    //
    float3 worldViewDir = normalize(gWorldEyePos - gWorldPos);

	float3 ambientIrradiance  = float3(0.0,0.0,0.0);
	float3 diffuseIrradiance  = float3(0.0,0.0,0.0);
	float3 specularIrradiance = float3(0.0,0.0,0.0);
	
    illuminance
	{
	    // Compute the light's reflection direction (R).
	    float3 wReflectDir = reflect(-gLightWorldDir, gWorldBumpedNormal);
    
        // Compute (L dot N), and (R dot V). 
	    // Clamp the results to the [0.001,1] range.
        // (Note: we don't clamp to [0,1] because
	    // potential floating-point innaccuracies could
	    // cause the value to become negative.
	    //
        float LdotN = max( dot(gLightWorldDir, gWorldBumpedNormal), 0.0 );
        float RdotV = max( dot(wReflectDir, worldViewDir), 0.001);

		// Accumulate the different types of irradiance.
        //
        ambientIrradiance += gLightAmbientColor;
        diffuseIrradiance += gLightDiffuseColor * LdotN;
        specularIrradiance += gLightSpecularColor * pow(RdotV, evaluate("cosinePower"));
    }

	diffuseIrradiance = evaluate("diffuse") * diffuseIrradiance;
	float3 opacityColor = float3(1.0,1.0,1.0) - evaluate("transparency");
	float3 diffuseAndAmbientColor = 
		(diffuseIrradiance + ambientIrradiance) * evaluate("color") * opacityColor;

	float3 specularColor = specularIrradiance * evaluate("specularColor");

	outRGBA = float4(diffuseAndAmbientColor + specularColor, 1.0);
}

#DEFINE_MATERIAL_PASS phong postLighting
material phong(out fragment float4 outRGBA)
{
	// Compute the view's reflection direction (R).
	float3 worldViewDir = normalize(gWorldEyePos - gWorldPos);
	float3 worldReflectedViewDir = reflect(-worldViewDir, gWorldBumpedNormal);

	float3 reflectedColor = evaluate(worldReflectedViewDir, "reflectedColor").rgb;
	reflectedColor = reflectedColor * evaluate("specularColor");

	float3 ambientColor = evaluate("ambientColor") * 
		evaluate("color") * (float3(1.0,1.0,1.0) - evaluate("transparency"));

	outRGBA  = float4(ambientColor, 1.0);
	outRGBA += float4(evaluate("reflectivity") * reflectedColor, 1.0);
	outRGBA += float4(evaluate("incandescence"), 1.0);
}

#DEFINE_MATERIAL_PASS phong alpha
material phong(out fragment float4 outRGBA)
{
	// TODO: Handle matte opacity, use background and other details.
	//
	float3 coloredOpacity = float3(1.0,1.0,1.0) - evaluate("transparency");
	outRGBA = float4(coloredOpacity, 1.0);
}



#//
#//				PhongE MATERIAL
#//
#DEFINE_MATERIAL_PASS phongE transparency
material phongE(out fragment float4 outRGBA)
{
	outRGBA = float4(evaluate("transparency"), 1.0);
}

#DEFINE_MATERIAL_PASS phongE lighting
material phongE(out fragment float4 outRGBA)
{
    // Calculate view direction in world space,
	// from point to eye.
    //
    float3 worldViewDir = normalize(gWorldEyePos - gWorldPos);

	float3 ambientIrradiance  = float3(0.0,0.0,0.0);
	float3 diffuseIrradiance  = float3(0.0,0.0,0.0);
	float3 specularIrradiance = float3(0.0,0.0,0.0);
	
    illuminance
	{
	    // Compute the light's reflection direction (R).
	    float3 wReflectDir = reflect(-gLightWorldDir, gWorldBumpedNormal);
    
        // Compute (L dot N), and (R dot V). 
	    // Clamp the results to the [0.001,1] range.
        // (Note: we don't clamp to [0,1] because
	    // potential floating-point innaccuracies could
	    // cause the value to become negative.
	    //
        float LdotN = max( dot(gLightWorldDir, gWorldBumpedNormal), 0.0 );
        float RdotV = max( dot(wReflectDir, worldViewDir), 0.001);

		// Accumulate the different types of irradiance.
        //
        ambientIrradiance += gLightAmbientColor;
        diffuseIrradiance += gLightDiffuseColor * LdotN;

		if( LdotN > 0.0 )
		{
			float roughness = evaluate("roughness");
			if (roughness < 0.000001)
				roughness = 0.000001;
			else {
				roughness  = (roughness * 0.969) + 0.03;
				roughness *= roughness;
			}
			
			float highLightSize = evaluate( "highlightSize" );
			float ihs = 1.0 - highLightSize;		
			float cosRV = RdotV;
				
			if (cosRV > ihs)
			{
				float aa  = (cosRV - ihs) / highLightSize;
				float tmp = aa * aa * (roughness - 1.0) + 1.0;
				if( tmp < 0.0001 )
					cosRV = 0.0;
				else
					cosRV = aa * roughness / tmp;
			
				if( LdotN < 0.1 && cosRV > 0.001 )
				{
					// need to correct value
					cosRV *= (LdotN * 10.0);
				}

				specularIrradiance += (gLightSpecularColor * cosRV);
			}
			
		}
    }

	float3 matColor  = evaluate( "color" );

	diffuseIrradiance = evaluate("diffuse") * diffuseIrradiance;
	float3 opacityColor = float3(1.0,1.0,1.0) - evaluate("transparency");
	float3 diffuseAndAmbientColor = (diffuseIrradiance + ambientIrradiance) * matColor * opacityColor;

	float3 whiteness = evaluate( "whiteness" );
	float3 inSpecColor = evaluate( "specularColor" );
	float3 specularColor = specularIrradiance * inSpecColor * ( whiteness + ( 1.0 - whiteness ) * matColor );

	outRGBA = float4(diffuseAndAmbientColor + specularColor, 1.0);
}

#DEFINE_MATERIAL_PASS phongE postLighting
material phongE(out fragment float4 outRGBA)
{
	// Compute the view's reflection direction (R).
	float3 worldViewDir = normalize(gWorldEyePos - gWorldPos);
	float3 worldReflectedViewDir = reflect(-worldViewDir, gWorldBumpedNormal);

	float3 reflectedColor = evaluate(worldReflectedViewDir, "reflectedColor").rgb;
	reflectedColor = reflectedColor * evaluate("specularColor");

	float3 ambientColor = evaluate("ambientColor") * 
		evaluate("color") * (float3(1.0,1.0,1.0) - evaluate("transparency"));

	outRGBA = float4(evaluate("reflectivity") * reflectedColor + ambientColor + evaluate("incandescence"), 1.0);

}

#DEFINE_MATERIAL_PASS phongE alpha
material phongE(out fragment float4 outRGBA)
{
	// TODO: Handle matte opacity, use background and other details.
	//
	float3 coloredOpacity = float3(1.0,1.0,1.0) - evaluate("transparency");
	outRGBA = float4(coloredOpacity, 1.0);
}


#//
#//				DirectionalLight LIGHT
#//

#DEFINE_LIGHT directionalLight
light directional_light_fragment(
				out fragment float3 outLightWorldDir,
				out fragment float3 outLightAmbientColor,
				out fragment float3 outLightDiffuseColor,
				out fragment float3 outLightSpecularColor,

				uniform matrix4 transform, 
				uniform sampler2DShadow shadowTex,
				uniform matrix4 dirWorldToProjLightMatrix,

				uniform float light_intensity,
				uniform float3 light_color,
				uniform float useDepthMapShadows,
								
				// These remaining uniforms are required for the
				// automatic creation of shading attributes which
				// are assumed to exist for light translation.
				//
				uniform float depthBias,
				uniform float nearCP,
				uniform float farCP,
				uniform float depthMapSize)
{
	outLightWorldDir = (mul(float4(0.0, 0.0, 1.0, 0.0), transform)).xyz;

	// Get the shadowed value.
	float unshadowedRatio = 1.0;
	if (useDepthMapShadows > 0.5)
	{
		float4 projTexCoords = mul(float4(gWorldPos, 1.0), dirWorldToProjLightMatrix);
		unshadowedRatio = shadow2DProj(shadowTex, projTexCoords);
	}

	float3 finalLightColor = unshadowedRatio * light_color * light_intensity;
	outLightDiffuseColor = finalLightColor;    
	outLightSpecularColor = finalLightColor;

	outLightAmbientColor = float3(0.0,0.0,0.0);
}


#DEFINE_LIGHT pointLight
light point_light_fragment(
				out fragment float3 outLightWorldDir,
				out fragment float3 outLightAmbientColor,
				out fragment float3 outLightDiffuseColor,
				out fragment float3 outLightSpecularColor,
				
				uniform float3 light_pos,
				uniform matrix4 transform,
				uniform float3 light_color, 
				uniform float light_intensity,
				uniform float lightDecayExponent,
								
				// These remaining uniforms are required for the
				// automatic creation of shading attributes which
				// are assumed to exist for light translation.
				//
				uniform float depthBias,
				uniform float nearCP, 
				uniform float farCP,
				uniform float depthMapSize, 
				uniform float useDepthMapShadows,
				uniform float4 light_decay)
{
	// Compute light vector, pointing toward the object to be lit.
	// [claforte] Should use the transform's position instead
	// of "light_pos".
	float3 wLightDir = light_pos - gWorldPos;
	float wLightDistance = length(wLightDir);
	outLightWorldDir = wLightDir / wLightDistance;

	// Compute the light's distance decay
	float undecayedRatio = 1.0 / pow(max(wLightDistance, 1.0), lightDecayExponent);

	float3 finalLightColor = undecayedRatio * light_intensity * light_color;
	outLightDiffuseColor = finalLightColor;    
	outLightSpecularColor = finalLightColor;

	outLightAmbientColor = float3(0.0,0.0,0.0);
}

#DEFINE_LIGHT ambientLight
light ambient_light_fragment(
				out fragment float3 outLightWorldDir,
				out fragment float3 outLightAmbientColor,
				out fragment float3 outLightDiffuseColor,
				out fragment float3 outLightSpecularColor,
				
				uniform float3 light_pos,
				uniform float3 light_color,
				uniform float light_intensity,
				uniform float ambient_shade)
{
	// Compute light vector, pointing toward the object to be lit.    
	float3 L = normalize(light_pos - gWorldPos);
	float LdotN = dot(L, gWorldBumpedNormal);
	float as = ambient_shade * (LdotN - 1.0); 

	outLightAmbientColor = light_color * light_intensity * (1.0 + as);

	// This light never contributes to diffuse or specular.
	outLightDiffuseColor = float3(0.0,0.0,0.0);
	outLightSpecularColor = float3(0.0,0.0,0.0);

	// The ambient light's direction is set to 0.
	outLightWorldDir = float3(0.0,0.0,0.0);
}

#DEFINE_LIGHT spotLight
light spot_light_fragment(
				out fragment float3 outLightWorldDir,
				out fragment float3 outLightAmbientColor,
				out fragment float3 outLightDiffuseColor,
				out fragment float3 outLightSpecularColor,
				
				uniform matrix4 transform,
				uniform float3 light_pos,
				uniform float3 light_color, 
				uniform float light_intensity,
				uniform float useDepthMapShadows,
				uniform sampler2D color_tex,
				uniform sampler2DShadow shadowTex,
				uniform float drop_off, 
				uniform matrix4 spotWorldToProjLightMatrix,
				uniform float spotCosPenumbra,
				uniform float spotCosUmbra,
				uniform float lightDecayExponent,
				uniform float lightScale,

				// These remaining uniforms are required for the
				// automatic creation of shading attributes which
				// are assumed to exist for light translation.
				//
				uniform float light_exponent, 
				uniform float light_cutOff,
				uniform float proj_tex, 
				uniform float depthBias,
				uniform float depthMapSize, 
				uniform float4 light_decay,
				uniform float cone_angle, 
				uniform float penumbra_angle,
				uniform float nearCP, 
				uniform float farCP)
{
	// Compute light vector, pointing toward the object to be lit.
	// [claforte] Should use the transform's position instead
	// of "light_pos".
	float3 wLightDir = light_pos - gWorldPos;
	float wLightDistance = length(wLightDir);
	outLightWorldDir = wLightDir / wLightDistance;

	float3 wSpotDir = (mul(float4(0.0, 0.0, 1.0, 0.0), transform)).xyz;

	// Compute the spot light's radial attenuation.
	// The spot cone of influence is divided into
	// two regions: the penumbra (outer region) and
	// umbra (inner region).
	// NOTE: at this point the penumbra angle is assumed
	// to be positive, and both the umbra and penumbra angle
	// are assumed to be greater than 0.
	//
	float SdotL = dot(outLightWorldDir, wSpotDir);
	float spotRadialIntensity;
	if (SdotL < spotCosPenumbra)
	{
		// The angle is larger than the total angle,
		// therefore it's outside of the cone.
		//
		spotRadialIntensity = 0.0;
	} 
	else
	{
		// Make an exponential decay over the inner region.
		//
		spotRadialIntensity = pow(SdotL, drop_off);

		if (SdotL < spotCosUmbra)
		{
			// Linearly interpolate between
			// the penumbra and umbra angle.
			//
			spotRadialIntensity = spotRadialIntensity * 
				(SdotL - spotCosPenumbra) / (spotCosUmbra - spotCosPenumbra);
		}
	}

	// Get the projected light color.
	float4 projTexCoords = mul(float4(gWorldPos, 1.0), spotWorldToProjLightMatrix);
	float3 projLightTexCoords = projTexCoords.xyz / projTexCoords.w;
	projLightTexCoords = (projLightTexCoords - 0.5) * lightScale + 0.5;
	float3 projLightColor = texture2D(color_tex, projLightTexCoords.xy).rgb;

	// Get the shadowed value.
	float unshadowedRatio = 1.0;
	if (useDepthMapShadows > 0.5)
	{
		unshadowedRatio = shadow2DProj(shadowTex, projTexCoords);
	}

	// Compute the light's distance decay
	float undecayedRatio = 1.0 / pow(wLightDistance, lightDecayExponent);

	float3 finalLightColor = (unshadowedRatio * undecayedRatio) * 
							 light_color * light_intensity * 
							 spotRadialIntensity * projLightColor;

	outLightDiffuseColor = finalLightColor;
	outLightSpecularColor = finalLightColor;

	outLightAmbientColor = float3(0.0,0.0,0.0);
}
