// Maya_blinn_fragment.cg
//

struct vert2frag
{
    float4 hPosition            : POSITION;
    float4 wPosition            : TEXCOORD0;
    float3 wBinormal            : TEXCOORD1;
	float4 colorTexUv			: TEXCOORD2;
	float3 wTangent				: TEXCOORD3;
	float4 bumpTexUv			: TEXCOORD4;
	float4 specTexUv			: TEXCOORD5;
	float4 transpTexUv			: TEXCOORD6;
};



// Uniforms common to projective light textures or shadow maps.
// 
#if defined(PROJ_LIGHT_TEXTURE) || defined(SHADOW_MAP)

#define UNIFORM_WORLD_TO_PROJECTIVE_LIGHT_MATRIX		\
			uniform float4  worldToProjLightMatrix0,	\
			uniform float4  worldToProjLightMatrix1,	\
			uniform float4  worldToProjLightMatrix2,	\
			uniform float4  worldToProjLightMatrix3,	

#else // (! (defined(PROJ_LIGHT_TEXTURE) || defined(SHADOW_MAP))

#define UNIFORM_WORLD_TO_PROJECTIVE_LIGHT_MATRIX

#endif // PROJ_LIGHT_TEXTURE || SHADOW_MAP


// Uniforms specific to shadow map.
//
#if defined(SHADOW_MAP)

#define UNIFORM_SHADOW_MAP_TEXTURE						\
			uniform sampler2D shadowTex,

#else // !defined(SHADOW_MAP)

#define UNIFORM_SHADOW_MAP_TEXTURE

#endif // SHADOW_MAP


// Uniforms specific to projective light texture.
//
#if defined(PROJ_LIGHT_TEXTURE)

#define UNIFORM_PROJ_LIGHT_TEXTURE						\
			uniform sampler2D lightColorTex,            

#else // !defined(PROJ_LIGHT_TEXTURE)

#define UNIFORM_PROJ_LIGHT_TEXTURE

#endif // PROJ_LIGHT_TEXTURE




#if defined(POINT_LIGHT)

#define LIGHT_SPECIFIC_UNIFORMS					\
			uniform float3  wLightPos,			\
            uniform float4  lightDecayCoeff,

#elif defined(SPOT_LIGHT)

#define LIGHT_SPECIFIC_UNIFORMS							\
			uniform float3  wLightPos,					\
            uniform float4  lightDecayCoeff,			\
			uniform float3  wSpotDir,					\
			uniform float	cosPenumbra,				\
			uniform float   cosUmbra,					\
			uniform float	radialDropOff,				\
			UNIFORM_WORLD_TO_PROJECTIVE_LIGHT_MATRIX	\
			UNIFORM_SHADOW_MAP_TEXTURE					\
			UNIFORM_PROJ_LIGHT_TEXTURE


#elif defined(DIR_LIGHT)

#define LIGHT_SPECIFIC_UNIFORMS							\
			uniform float3  wLightDir,					\
			UNIFORM_WORLD_TO_PROJECTIVE_LIGHT_MATRIX	\
			UNIFORM_SHADOW_MAP_TEXTURE					


#elif defined(AMBIENT_LIGHT)

#define LIGHT_SPECIFIC_UNIFORMS					\
			uniform float3  wLightPos,			\
			uniform float	ambientShade,

#endif // Light-specific uniforms




float4 main(vert2frag        IN,
			uniform float4  gWorldEyePos,
			uniform float3	lightColor,		// pre-multiplied by intensity.
			LIGHT_SPECIFIC_UNIFORMS
			uniform sampler2D colorTex,
			uniform float4	colorTexMatrix0,
			uniform float4	colorTexMatrix1,

			uniform sampler2D specTex,
			uniform float4	specTexMatrix0,
			uniform float4	specTexMatrix1,

			uniform sampler2D bumpTex,
			uniform float4	bumpTexMatrix0,
			uniform float4	bumpTexMatrix1,

			uniform sampler2D transpTex,
			uniform float4	transpTexMatrix0,
			uniform float4	transpTexMatrix1,

            uniform float   diffuseCoeff,
			uniform float	rollOff,
			uniform float	eccentricity) : COLOR
{ 
    // Compute the unnormalized light direction vector, expressed
    // in world space.
    //

#if defined(POINT_LIGHT) || defined(SPOT_LIGHT) || defined(AMBIENT_LIGHT)
    float3 wLightVector = (wLightPos - IN.wPosition.xyz).rgb;
    float3 wLightDir = normalize(wLightVector);
#elif defined(DIR_LIGHT)
	// do nothing. wLightDir is read from the uniform.
#endif //0

    // Calculate view direction in world space,
	// from point to eye.
    //
    float3 wViewDir = normalize(gWorldEyePos.xyz - IN.wPosition.xyz);

	float3 wTangent = normalize(IN.wTangent);
	float3 wNormal = normalize(cross(wTangent, IN.wBinormal));
	float3 wBinormal = cross(wNormal, wTangent);

	// translate and scale texture coordinates
	float2 placedBumpTexUv;
	float4 bumpTexUv = float4(IN.bumpTexUv.xy, 0.0, 1.0);
	placedBumpTexUv.x = dot(bumpTexMatrix0, bumpTexUv);
	placedBumpTexUv.y = dot(bumpTexMatrix1, bumpTexUv);

	float3 tBumpedNormal = normalize(f3tex2D(bumpTex, placedBumpTexUv) * 2 - 1);

	float3 wBumpedNormal;
	wBumpedNormal = tBumpedNormal.x * wTangent + 
		            tBumpedNormal.y * wBinormal + 
		            tBumpedNormal.z * wNormal;

	// To support transparency and back-face lighting,
	// apply the following logic:
	// if the geometric normal is pointing away from the
	// observer, revert the bumped normal.
	// this is broken, need to to this after projection
	//if (dot(wViewDir, wNormal) < 0)
	//{
	//	wBumpedNormal = -wBumpedNormal;
	//}

    float3 wReflectDir = reflect(-wLightDir, wBumpedNormal);
    
    // Compute (L dot N), and (R dot V). Clamp the results to the [0,1] range.
    //
    float LDotN = dot(wLightDir, wBumpedNormal);

#if !defined(AMBIENT_LIGHT)
    LDotN = max(LDotN, 0);
#endif // not ambient light
    //    
    float RDotV = dot(wReflectDir, wViewDir);
    RDotV = max(RDotV, 0.001);



#ifdef BLINN_SHADING_MODEL

	float cosne = dot(wViewDir, wBumpedNormal);
	
	float3 specularColor;

	float cosln = dot(wLightDir, wBumpedNormal);
	if( cosln > 0.0f )
	{
		float3 wHalfAngleDir = normalize(wLightDir + wViewDir);

		float coseh = dot(wHalfAngleDir, wViewDir);
		float cosnh = dot(wHalfAngleDir, wBumpedNormal);

		// We want the range of eccentricity to go from 0-1 for
		// 2 reasons. 1. UI looks much nicer. 2. texture mapping the value is much simpler
        // when the effective range of this attribute runs from 0 to 1, rather than -0.765 to
		// -2 or what ever it is otherwise. APP 09may97
		float computedEcc = eccentricity * eccentricity - 1.0;
		//
		float Dd = ( computedEcc + 1.0f) / ( 1.0 + computedEcc * cosnh * cosnh );
		Dd    = Dd * Dd;
		cosnh = 2 * cosnh;

		float Gg;
		if (cosne < cosln) 
			Gg = (cosne*cosnh < coseh) ? cosnh / coseh : 1.0f / cosne;
		else 
			Gg = (cosln*cosnh < coseh) ? cosln * cosnh / (coseh * cosne) : 1.0f / cosne;
		
		// Fresnel calculation.
		//
		coseh = 1.0f - coseh;
		coseh = coseh * coseh * coseh;
		float Ff = coseh + (1.0f - coseh) * rollOff;

		// Make sure that the specularCoefficient doesn't become negative.
		//
		float specularCoefficient = max(Dd * Gg * Ff, 0);
		float3 specularColor = specularCoefficient * attenuatedLightColor;
	}

#endif // BLINN_SHADING_MODEL



#if defined(SHADOW_MAP) || defined(PROJ_LIGHT_TEXTURE)
	// Transform the world space position into proj light space.
	// This is required for shadow maps or projective light texture
	// look-ups.
	//
	float4 projTexCoords;

	float4 pos = float4(IN.wPosition.xyz, 1);
	projTexCoords.x = dot(pos, worldToProjLightMatrix0);
	projTexCoords.y = dot(pos, worldToProjLightMatrix1);
	projTexCoords.z = dot(pos, worldToProjLightMatrix2);
	projTexCoords.w = dot(pos, worldToProjLightMatrix3);
#endif // (SHADOW_MAP || PROJ_LIGHT_TEXTURE)


    // Light distance in world space, required to compute the
    // light attenuation.
    //
	float3 attenuatedLightColor = lightColor;
            
#if defined(PROJ_LIGHT_TEXTURE)
	// This is a spot light, where the light color comes from a 
	// projective texture.
	//

	float2 projLightTexCoords = projTexCoords.xy / projTexCoords.w;
	attenuatedLightColor = f3tex2D(lightColorTex, projLightTexCoords);

	// should be this
    //attenuatedLightColor = f3tex2Dproj(lightColorTex, projTexCoords.xyw);

	//float2 projLightTexCoords = projTexCoords.xy / projTexCoords.w;
	//projLightTexCoords.xy = projLightTexCoords.xy * 0.5 + 0.5;
	//attenuatedLightColor = f3tex2D(lightColorTex, projLightTexCoords);
#endif // PROJ_LIGHT_TEXTURE

	float3 tmpColor = float3(0,0,0);

#if defined(SHADOW_MAP)

	#if defined(ARB_FRAGMENT_PROGRAM_PROFILE)	
		// Simulate the GL_ARB_shadow extension fixed function operation where:
		// TEXTURE_COMPARE_MODE = COMPARE_R_TO_TEXTURE and TEXTURE_COMPARE_FUNC = LEQUAL
		float Dt = f1tex2Dproj(shadowTex, projTexCoords.xyww);
		float R  = projTexCoords.z / projTexCoords.w;
		float inShadow = R <= Dt ? 1.0 : 0.0;
	#else	
		float inShadow = f1texcompare2D(shadowTex, projTexCoords); 
	#endif
	
	// non projected
	//float3 projShadowTexCoords = projTexCoords.xyz / projTexCoords.w;
	//float inShadow = f1texcompare2D(shadowTex, projShadowTexCoords.xyz);

	//tmpColor = float3(projShadowTexCoords.z, projShadowTexCoords.z, projShadowTexCoords.z);
	//tmpColor = float3(inShadow, inShadow, inShadow);

	attenuatedLightColor = attenuatedLightColor * inShadow;
#endif // SHADOW_MAP


	float attenuation = 1;
#if defined(POINT_LIGHT) || defined(SPOT_LIGHT)

	float wLightDistance = length(wLightVector);

    // Calculate the attenuation of the light
	// [claforte] Could be optimized if necessary.
	//
    attenuation = lightDecayCoeff.x + 
                lightDecayCoeff.y * wLightDistance + 
                lightDecayCoeff.z * wLightDistance * wLightDistance +
				lightDecayCoeff.w * wLightDistance * wLightDistance * wLightDistance;

    attenuation = max(attenuation, 1);
#endif // attenuation


	float spotRadialIntensity = 1;
#if defined(SPOT_LIGHT)

	// Compute the radial attenuation.
	// The spot cone of influence is divided into
	// two regions: the penumbra (outer region) and
	// umbra (inner region). 
	// For more info google: "D3D lighting equation, 
	// 3rd revision", Doug Rogers (nVidia).
	//

	float SdotL = dot(wLightDir, wSpotDir);
	
	if (SdotL < cosPenumbra)
	{
		// The angle is larger than the total angle,
		// therefore it's outside of the cone.
		spotRadialIntensity = 0;
	} 
	else
	{
		spotRadialIntensity = pow(SdotL, radialDropOff);

		if (SdotL < cosUmbra)
		{
			// Linearly interpolate between
			// the penumbra and umbra angle.
			//
			spotRadialIntensity = spotRadialIntensity * 
				(SdotL - cosPenumbra) / (cosUmbra - cosPenumbra);
		}
	}

#endif // SPOT_LIGHT

	attenuatedLightColor = attenuatedLightColor * 
						spotRadialIntensity / attenuation;


	// after applying the placement matrix 
	float2 placedColorTexUv;
	float4 colorTexUv = float4(IN.colorTexUv.xy, 0.0, 1.0);
	placedColorTexUv.x = dot(colorTexMatrix0, colorTexUv);
	placedColorTexUv.y = dot(colorTexMatrix1, colorTexUv);

	float4 baseColor = f4tex2D(colorTex, placedColorTexUv);

    float3 diffuseColor = baseColor.rgb * diffuseCoeff * LDotN;
	
	float2 placedSpecTexUv;
	float4 specTexUv = float4(IN.specTexUv.xy, 0.0, 1.0);
	placedSpecTexUv.x = dot(specTexMatrix0, specTexUv);
	placedSpecTexUv.y = dot(specTexMatrix1, specTexUv);
	float4 fetchedSpecColor = f4tex2D(specTex, placedSpecTexUv);
	
	float specularExp = fetchedSpecColor.a * 128;

#if defined(AMBIENT_LIGHT)
	ambientShade = ambientShade * (LDotN - 1);
	float4 finalColor = float4(lightColor * baseColor.rgb * (1 + ambientShade).xxx, 1);
#else
    float3 specularColor = specularColor * fetchedSpecColor.rgb;
	float4 finalColor = float4(attenuatedLightColor * (diffuseColor + specularColor), 1.0);
#endif // non-ambient light

	// translate and scale texture coordinates
	float2 placedTranspTexUv;
	float4 transpTexUv = float4(IN.transpTexUv.xy, 0.0, 1.0);
	placedTranspTexUv.x = dot(transpTexMatrix0, transpTexUv);
	placedTranspTexUv.y = dot(transpTexMatrix1, transpTexUv);

	float4 transpColor = f4tex2D(transpTex, placedTranspTexUv);

#if defined(OPACITY_IN_COLOR_ALPHA)
	// The opacity is encoded in the base color texture's alpha.
	//
	finalColor = finalColor * baseColor.a;
	finalColor.a = baseColor.a;
	
#else	// TRANSP_IN_TRANSP_RGB
	// The transparency color is encoded in the transparency texture's RGB,
	// and the opacity is stored in the transparency texture's alpha.
	//
	finalColor.a = transpColor.a;
	finalColor = finalColor * (1-transpColor);
#endif // 

	return finalColor;
}
