// phong_fragment.cg
//
// This file cannot be directly compiled through CG... it first must be
// parsed to insert channel-specific instructions.
//
// For example:
// cgc.exe "$(InputPath)" -profile fp30 -o $(InputName).fp -DSPOT_LIGHT -DTRANSP_IN_TRANSP_RGB -DPROJ_LIGHT_TEXTURE

struct vert2frag
{
    float4 hPosition            : POSITION;
#if defined(COLOR_PER_VERTEX_CHANNEL_all)
	float4	colorPerVertex		: COLOR0;
#else

	#if defined(BUMP_MAP)
		float3 wBinormal            : TEXCOORD0;
		float3 wTangent				: TEXCOORD1;
		float4 bumpTexUv			: TEXCOORD2;
	#else
		float3 wNormal				: TEXCOORD0;
	#endif // BUMP_MAP
	
#if defined(TWO_SIDED_LIGHTING)
	float4	frontColor		: COLOR1; // Secondary front color
	float4	backColor		: BCOL1;  // Seondary back color
#endif // TWO_SIDED_LIGHTING

	#if defined(COLOR_PER_VERTEX)
		// Optional color per vertex
		float4	colorPerVertex		: COLOR0;
	#endif // COLOR_PER_VERTEX

#if defined(COLOR_PER_VERTEX_MASK)
	// Optional color per vertex mask, mapped to
	// user attribute 0
	float colorPerVertexMask;
#endif	
	
	#if !defined(CONSTANT_TRANSPARENCY)	
		float4 transpTexUv			: TEXCOORD3;
	#endif	
	#if !defined(CONSTANT_INCAND)	
		float4 incanTexUv			: TEXCOORD4;
	#endif
	#if !defined(CONSTANT_REFLECTIVITY)		
		float4 reflectivityTexUv	: TEXCOORD5;
	#endif	
		float4 wPosition			: TEXCOORD6;

#endif // COLOR_PER_VERTEX_CHANNEL_all
};

float4 main(vert2frag        IN
#if !defined(COLOR_PER_VERTEX_CHANNEL_all)
			,
			uniform float4  gWorldEyePos,
	#if !defined(TWO_SIDED_LIGHTING)			
			uniform float	normalMultiplier,
	#endif

	#if defined(CONSTANT_INCAND)
			uniform float3 constantIncand,
	#endif
	#if defined(BUMP_MAP)
			uniform sampler2D bumpTex,
			uniform float4	bumpTexMatrix0,
			uniform float4	bumpTexMatrix1,
	#endif // BUMP_MAP

			//[claforte] May one day be used to mimic software.
			//uniform sampler2D specTex,
			//uniform float4	specTexMatrix0,
			//uniform float4	specTexMatrix1,

	#if !defined(CONSTANT_TRANSPARENCY)			
			uniform sampler2D transpTex,
			uniform float4	transpTexMatrix0,
			uniform float4	transpTexMatrix1,
	#else			
			uniform float3	constantTransparency,
	#endif			

			uniform sampler2D incanTex,
			uniform float4	incanTexMatrix0,
			uniform float4	incanTexMatrix1,

	#if defined(CONSTANT_REFLECTED)	
			uniform float3 constantReflected,
	#else
		#if defined(REFLECT_CUBE_LOOKUP) || defined(REFRACT_CUBE_LOOKUP)
			uniform float3 worldToCubeRotationMatrix0,
			uniform float3 worldToCubeRotationMatrix1,
			uniform float3 worldToCubeRotationMatrix2,
			uniform samplerCUBE reflectedColTex,
		#endif // (REFLECT_CUBE_LOOKUP) || (REFRACT_CUBE_LOOKUP)

		#if defined(REFLECT_SPHERE_LOOKUP)
			uniform sampler2D reflectedColTex,
		#endif
	
		#if defined (REFRACT_CUBE_LOOKUP)
			// Refractive indices for red, green, blue.
			uniform float3 refractiveIndices,
		#endif				
	#endif		

	#if defined(ENVIRONMENT_RAMP_LOOKUP)
		#if defined(CONSTANT_ENVIRONMENT)	
			uniform float3 constantEnvironment,
		#else
			uniform sampler2D environmentColTex,
		#endif
	#endif

	#if !defined(CONSTANT_REFLECTIVITY)
			uniform sampler2D reflectivityTex,
			uniform float4	reflectivityTexMatrix0,
			uniform float4	reflectivityTexMatrix1
	#else			
			uniform float	constantReflectivity
	#endif
			
#endif // !COLOR_PER_VERTEX_CHANNEL_all		
			) : COLOR
{ 
#if defined(COLOR_PER_VERTEX_CHANNEL_all)
	// We are replacing everything. Just set the final color
	// to be the CPV color.
	float4 finalColor = float4(IN.colorPerVertex.rgb * IN.colorPerVertex.aaa, 1);
#else

    // Calculate view direction in world space,
	// from point to eye.
    //
    float3 wViewDir = normalize(gWorldEyePos.xyz - IN.wPosition.xyz);

#if defined(TWO_SIDED_LIGHTING)
	float faceMultiplier = IN.frontColor.x > 0.3 ? 1.0 : -1.0;
#else
	float faceMultiplier = normalMultiplier;
#endif	

#if defined(BUMP_MAP)
	// Compute an orthonomal basis to transform from tangent space
	// to world space, and vice-versa.
	//
	float3 wTangent = normalize(IN.wTangent);
	float3 wNormal = normalize(cross(wTangent, IN.wBinormal));
	float3 wBinormal = cross(wNormal, wTangent);

	// Translate and scale bump texture coordinates, by multiplying
	// the uvs by the bump texture matrix.
	//
	float2 placedBumpTexUv;
	float4 bumpTexUv = float4(IN.bumpTexUv.xy, 0.0, 1.0);
	placedBumpTexUv.x = dot(bumpTexMatrix0, bumpTexUv);
	placedBumpTexUv.y = dot(bumpTexMatrix1, bumpTexUv);

	// Fetch the bumped normal (in tangent space) from the bump texture.
	// The (... * 2 - 1) at the end is used to unpack the normal components,
	// from the [0,1] to [-1, -1] range.
	//
	float3 tBumpedNormal = normalize(f3tex2D(bumpTex, placedBumpTexUv) * 2 - 1);

	#if defined(INVERT_TANGENT_SPACE_BASIS)
		 faceMultiplier = -faceMultiplier;		
	#endif							

	// Compute the world-space bumped normal.
	//
	float3 wBumpedNormal = faceMultiplier * tBumpedNormal.x * wTangent + 
						faceMultiplier * tBumpedNormal.y * wBinormal + 
						tBumpedNormal.z * wNormal;

#else  // !defined(BUMP_MAP)
	float3 wNormal = normalize(IN.wNormal);
	float3 wBumpedNormal = wNormal;	
#endif // BUMP_MAP

	// To support transparency and back-face lighting,
	// allow normal inversion.
	//
	wNormal = faceMultiplier * wNormal;
	wBumpedNormal = faceMultiplier * wBumpedNormal;

	// Compute the viewer's reflection direction.
	//
#if !defined(CONSTANT_REFLECTED) || defined(ENVIRONMENT_RAMP_LOOKUP)
	float3 wReflectDir = reflect(-wViewDir, wBumpedNormal);
#endif

#if defined(CONSTANT_REFLECTED)	
	float3 reflectedColor = constantReflected;
#else

	#if defined(REFLECT_CUBE_LOOKUP)

		// Transform the reflection vector from world space to
		// cube space. Ideally, we'd take the full cube placement
		// information into account, but since this is quite
		// complicated, we use the traditional OpenGL-shortcut of 
		// only applying a rotation.
		//
		float3 cReflectDir;
		cReflectDir.x = dot(wReflectDir, worldToCubeRotationMatrix0);
		cReflectDir.y = dot(wReflectDir, worldToCubeRotationMatrix1);
		cReflectDir.z = dot(wReflectDir, worldToCubeRotationMatrix2);
		float3 reflectedColor = f3texCUBE(reflectedColTex, cReflectDir);

	#elif defined(REFLECT_SPHERE_LOOKUP)

		// This calculation is directly taken from the OpenGL spec's sphere
		// mapping section.
		//
		float x = wReflectDir.x;
		float y = wReflectDir.y;
		float z = wReflectDir.z + 1;
		float m = 2.0 * sqrt(x * x + y * y + z * z);

		float2 sphereUv = wReflectDir.xy / m + 0.5;
		float3 reflectedColor = f3tex2D(reflectedColTex, sphereUv);

	#else

		// Set the reflected color to black.
		float3 reflectedColor = float3(0, 0, 0);

	#endif
#endif


#if !defined(CONSTANT_REFLECTIVITY)
	// Apply the reflectivity lookup then fetch the appropriate texel.
	//
	float2 placedReflectivityTexUv;
	
	#if defined(CAMERA_ANGLE_REFLECTIVITY_UVS)
		float EDotN = dot(wBumpedNormal, wViewDir);
		EDotN = max(EDotN, 0.0);
		placedReflectivityTexUv.x = EDotN;
		placedReflectivityTexUv.y = 0.5;
		float reflectivity = f4tex2D(reflectivityTex, placedReflectivityTexUv).r;
		reflectivity = max(reflectivity, 0.0);
	#else
		float4 reflectivityTexUv = float4(IN.reflectivityTexUv.xy, 0.0, 1.0);
		placedReflectivityTexUv.x = dot(reflectivityTexMatrix0, reflectivityTexUv);
		placedReflectivityTexUv.y = dot(reflectivityTexMatrix1, reflectivityTexUv);
		float reflectivity = f4tex2D(reflectivityTex, placedReflectivityTexUv).r;
	#endif	
#else
	float reflectivity = constantReflectivity;
#endif	

#if defined(ENVIRONMENT_RAMP_LOOKUP)
		
	// This is taken from Maya's ramp shader lookup
	// for it's "environment" lookup.
	//
	if (reflectivity > 0.0)
	{
		#if defined(CONSTANT_ENVIRONMENT)	
			reflectedColor += constantEnvironment;
		#else
			float2 rampUv;
			rampUv.x = ( wReflectDir.y + 1.0 ) * 0.5;
			rampUv.y = 0.5;
			reflectedColor += f3tex2D(environmentColTex, rampUv);		
		#endif
	}
#endif

	// Apply either the incandescence texture matrix to the corresponding texture 
	// coordinates, then fetch the appropriate texel, 
	//
	// or
	//
	// apply the color per vertex if this channel is overridden.
	//
#if defined(COLOR_PER_VERTEX) 

	// CPV code path
	#if !defined(CONSTANT_INCAND)
		// Textured incandescence
		#if !defined(CAMERA_ANGLE_INCAND_UVS)
			float2 placedIncanTexUv;
			float4 incanTexUv = float4(IN.incanTexUv.xy, 0.0, 1.0);
			placedIncanTexUv.x = dot(incanTexMatrix0, incanTexUv);
			placedIncanTexUv.y = dot(incanTexMatrix1, incanTexUv);
			float3 incandescenceTex = f3tex2D(incanTex, placedIncanTexUv);
		#else
			float2 placedIncanTexUv;
			// Don't recompute twice if already done for reflectivity
			#if !defined(CAMERA_ANGLE_REFLECTIVITY_UVS)			
				float EDotN = dot(wViewDir, wBumpedNormal);
				EDotN = max(EDotN, 0.001);
			#endif	
			placedIncanTexUv.x = EDotN;
			placedIncanTexUv.y = 0.5;
			float3 incandescenceTex = f3tex2D(incanTex, placedIncanTexUv);
		#endif
	#else
		float3 incandescenceTex = constantIncand;
	#endif	

	// CPV incandescence
	float3 incandescenceCPV = IN.colorPerVertex.rgb * IN.colorPerVertex.aaa;

	#if defined(COLOR_PER_VERTEX_MASK)
		float3 mask = IN.colorPerVertexMask.rrrr;
		float3 invMask = float3(1.0, 1.0, 1.0) - mask;
		
		#if defined(COLOR_PER_VERTEX_OPERATOR_replace)
			float3 incandescence = (incandescenceCPV * mask) + (incandescenceTex * invMask);
		#elif defined(COLOR_PER_VERTEX_OPERATOR_add)
			float3 incandescence = incandescenceTex + (incandescenceCPV * mask);
		#elif defined(COLOR_PER_VERTEX_OPERATOR_subtract)
			float3 incandescence = incandescenceTex - (incandescenceCPV * mask);
		#elif defined(COLOR_PER_VERTEX_OPERATOR_modulate)
			float3 incandescence = incandescenceTex * (incandescenceCPV * mask);
		#elif defined(COLOR_PER_VERTEX_OPERATOR_divide)
			float3 incandescence = incandescenceTex / (incandescenceCPV * mask);
		#elif defined(COLOR_PER_VERTEX_OPERATOR_average)
			float3 incandescence = (incandescenceTex  + (incandescenceCPV * mask)) / 2;
		#elif defined(COLOR_PER_VERTEX_OPERATOR_modulate2x)
			float3 incandescence = (incandescenceTex * (incandescenceCPV * mask)) * 2.0;
		#else		
			float3 incandescence = incandescenceCPV;
		#endif
	#else	
		#if defined(COLOR_PER_VERTEX_OPERATOR_replace)
			float3 incandescence = incandescenceCPV;	
		#elif defined(COLOR_PER_VERTEX_OPERATOR_add)
			float3 incandescence = incandescenceTex + incandescenceCPV;
		#elif defined(COLOR_PER_VERTEX_OPERATOR_subtract)
			float3 incandescence = incandescenceTex - incandescenceCPV;
		#elif defined(COLOR_PER_VERTEX_OPERATOR_modulate)
			float3 incandescence = incandescenceTex * incandescenceCPV;
		#elif defined(COLOR_PER_VERTEX_OPERATOR_divide)
			float3 incandescence = incandescenceTex / incandescenceCPV;
		#elif defined(COLOR_PER_VERTEX_OPERATOR_average)
			float3 incandescence = (incandescenceTex  + incandescenceCPV) / 2;
		#elif defined(COLOR_PER_VERTEX_OPERATOR_modulate2x)
			float3 incandescence = (incandescenceTex * incandescenceCPV) * 2.0;
		#else		
			float3 incandescence = incandescenceCPV;
		#endif
	#endif // _COLOR_PER_VERTEX_MASK

#else
	// Non CPV code path
	#if !defined(CONSTANT_INCAND)

		#if !defined(CAMERA_ANGLE_INCAND_UVS)
			float2 placedIncanTexUv;
			float4 incanTexUv = float4(IN.incanTexUv.xy, 0.0, 1.0);
			placedIncanTexUv.x = dot(incanTexMatrix0, incanTexUv);
			placedIncanTexUv.y = dot(incanTexMatrix1, incanTexUv);
			float3 incandescence = f3tex2D(incanTex, placedIncanTexUv);
		#else
			float2 placedIncanTexUv;
			// Don't recompute twice if already done for reflectivity
			#if !defined(CAMERA_ANGLE_REFLECTIVITY_UVS)			
				float EDotN = dot(wViewDir, wBumpedNormal);
				EDotN = max(EDotN, 0.0);
			#endif				
			placedIncanTexUv.x = max(EDotN, 0.001);
			placedIncanTexUv.y = 0.5;
			float3 incandescence = f3tex2D(incanTex, placedIncanTexUv);
		#endif
	#else
		float3 incandescence = constantIncand;
	#endif	
#endif

	float3 refractedColor = 0;

#if defined(REFRACT_CUBE_LOOKUP)

	float3 opacityColor;

#if defined(CONSTANT_TRANSPARENCY)
	opacityColor = constantTransparency;
#else
	// Apply the transparency texture matrix to the corresponding texture 
	// coordinates, then fetch the appropriate texel.
	//
	float2 placedTranspTexUv;
	float4 transpTexUv = float4(IN.transpTexUv.xy, 0.0, 1.0);
	placedTranspTexUv.x = dot(transpTexMatrix0, transpTexUv);
	placedTranspTexUv.y = dot(transpTexMatrix1, transpTexUv);
	float4 fetchedTranspColor = f4tex2D(transpTex, placedTranspTexUv);

	#if defined(OPACITY_IN_COLOR_ALPHA)
		// The opacity is encoded in the base color texture's alpha.
		// (NOTE: the cpp code is expected to bind the color_tex texture
		// to the transpTex sampler2D.)
		//
		opacityColor = fetchedTranspColor.aaa;

	#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.
		//
		opacityColor = 1-fetchedTranspColor.rgb;
	#endif // 
#endif	

#if defined(COLOR_PER_VERTEX) 
	// Post-modulate any textured opacity with CPV opacity
	#if defined(COLOR_PER_VERTEX_MASK)
		opacityColor = opacityColor * IN.colorPerVertex.aaa * colorPerVertexMask;
	#else	
		opacityColor = opacityColor * IN.colorPerVertex.aaa;
	#endif
#endif
	
	// Compute the refracted color.
	float3 cViewDir;
	cViewDir.x = dot(-wViewDir, worldToCubeRotationMatrix0);
	cViewDir.y = dot(-wViewDir, worldToCubeRotationMatrix1);
	cViewDir.z = dot(-wViewDir, worldToCubeRotationMatrix2);

	float3 cBumpedNormal;
	cBumpedNormal.x = dot(wBumpedNormal, worldToCubeRotationMatrix0);
	cBumpedNormal.y = dot(wBumpedNormal, worldToCubeRotationMatrix1);
	cBumpedNormal.z = dot(wBumpedNormal, worldToCubeRotationMatrix2);

	// TODO: implement and test chromatic abherration.
	//
	float3 cRefractDir = refract(cViewDir, cBumpedNormal, refractiveIndices.r);
	refractedColor = f3texCUBE(reflectedColTex, cRefractDir);

	// Modulate the refracted color by the transparency.
	// (ie: a highly transparent surface will translate into a highly
	// refractive surface.)
	//
	refractedColor.rgb = refractedColor * (1-opacityColor);

#endif // REFRACT_CUBE_LOOKUP

	float4 finalColor;
	finalColor.rgb = reflectivity * reflectedColor + incandescence + refractedColor;
	finalColor.a = 1;

#endif // COLOR_PER_VERTEX_all
	return finalColor;

}
