/*=============================================================================
	BasePassVertexShader.usf: Base pass vertex shader
	Copyright 1998-2010 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

#define NEEDS_LIGHTMAP_COORDINATE		(TEXTURE_LIGHTMAP || SIMPLE_TEXTURE_LIGHTMAP || STATICLIGHTING_TEXTUREMASK)
#define NEEDS_SHADOWMAP_COORDINATE		STATICLIGHTING_TEXTUREMASK
#define NEEDS_VERTEX_LIGHTMAP			VERTEX_LIGHTMAP
#define NEEDS_SIMPLE_VERTEX_LIGHTMAP	SIMPLE_VERTEX_LIGHTMAP
#define NEEDS_BASEPASS_FOGGING			(MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE) && !DECAL_FACTORY

// SH lighting requires a TangentToWorld matrix.
#if ENABLE_SH_LIGHT
	#undef WORLD_COORDS
	#define WORLD_COORDS 1
#endif

#if DYNAMICALLY_SHADOWED_BASEPASS_LIGHT
	#undef WORLD_POS
	#define WORLD_POS 1
#endif

#include "Common.usf"
#include "Material.usf"
#include "VertexFactory.usf"

#if NEEDS_BASEPASS_FOGGING
	#include "HeightFogCommon.usf"
	#include "FogVolumeCommon.usf"
#endif

#if ENABLE_TRANSLUCENCY_DOF
	#include "DepthOfFieldCommon.usf"
#endif

#include "BasePassCommon.usf"

/** World space light direction for the dynamic light */
float4 LightDirectionAndbDirectional;
float4 LightPositionAndInvRadius; // w = 1.0 / Radius

/**
 * Scale factors for the light-map coefficients.
 * The light-map scale array is a set of float4s so it can be set as a contiguous chunk regardless of platform shader constant alignment.
 */
float4 LightMapScale[NUM_LIGHTMAP_COEFFICIENTS];

/** Intersecting fog volume color */
half3 ApproxFogColor;

/** Minimum extrema of the AABB of the intersecting fog volume */
float3 FogVolumeBoxMin;

/** Maximum extrema of the AABB of the intersecting fog volume */
float3 FogVolumeBoxMax;

/*
* Combines height fog and fog volume contribution.  Height fog is always applied after the fog volume 
* since height fog is infinite in extent and fog volumes are bounded.
*/
half4 GetCombinedFog(half4 HeightFogContribution,half4 FogVolumeContribution)
{
	//filter fog volume color as if it were coming through the height fog, and add in the emitted color from height fog
	//alpha stores the amount of original scene color to be transmitted, which is attenuated by both fog factors
	return half4(
		FogVolumeContribution.rgb * HeightFogContribution.a + HeightFogContribution.rgb,
		FogVolumeContribution.a * HeightFogContribution.a
		);
}

/** Computes fogging contribution for a given line integral. */
half4 ComputeFogContribution(float LineIntegral)
{
	//find the fog volume factor by evaluating the transmittance function
	half FogVolumeFactor = exp2(-LineIntegral);
	//weight fog volume color by 1 - fog volume factor
	half3 FogVolumeColor = (1.0f - FogVolumeFactor) * ApproxFogColor;

	return half4(FogVolumeColor,FogVolumeFactor);
}

#if FOGVOLUMEDENSITY_NONE || DECAL_FACTORY
	/** Computes fog contribution from no fog volume. */
	half4 GetFogDensity(float4 WorldPosition)
	{
		return half4(0,0,0,1);
	}
#endif

#if FOGVOLUMEDENSITY_CONSTANT && !DECAL_FACTORY
	/**
	 * Computes fog contribution for a constant density fog volume.
	 * The fog volume contribution is clipped by the AABB of the fog volume.
	 */
	half4 GetFogDensity(float4 WorldPosition)
	{
		//clip the ray from the vertex to the camera by the AABB of the fog volume 
		float2 BoxIntersections = RayBoxIntersect(WorldPosition.xyz, CameraPosition.xyz, FogVolumeBoxMin, FogVolumeBoxMax); 
		//get the fog volume line integral
		half LineIntegral = ConstantDensityLineIntegral(WorldPosition.xyz, CameraPosition.xyz, BoxIntersections);
		// Compute the fog volume contribution from the integral.
		return ComputeFogContribution(LineIntegral);
	}
#endif

#if FOGVOLUMEDENSITY_LINEARHALFSPACE && !DECAL_FACTORY
	/**
	 * Computes fog contribution for a linear halfspace density fog volume.
	 * The fog volume contribution is clipped by the AABB of the fog volume.
	 */
	half4 GetFogDensity(float4 WorldPosition)
	{
		//clip the ray from the vertex to the camera by the AABB of the fog volume 
		float2 BoxIntersections = RayBoxIntersect(WorldPosition.xyz, CameraPosition.xyz, FogVolumeBoxMin, FogVolumeBoxMax); 
		//get the fog volume line integral
		half LineIntegral = LinearHalfspaceLineIntegral(WorldPosition.xyz, CameraPosition.xyz, BoxIntersections);
		// Compute the fog volume contribution from the integral.
		return ComputeFogContribution(LineIntegral);
	}
#endif

#if FOGVOLUMEDENSITY_SPHEREDENSITY && !DECAL_FACTORY
	/**
	 * Computes fog contribution for a spherical density fog volume.
	 */
	half4 GetFogDensity(float4 WorldPosition)
	{
		//get the fog volume line integral
		half LineIntegral = SphericalLineIntegral(WorldPosition.xyz, CameraPosition.xyz);
		// Compute the fog volume contribution from the integral.
		return ComputeFogContribution(LineIntegral);
	}
#endif

#if FOGVOLUMEDENSITY_CONEDENSITY && !DECAL_FACTORY
	/**
	 * Computes fog contribution for a spherical density fog volume.
	 */
	half4 GetFogDensity(float4 WorldPosition)
	{
		// Cone fog integral isn't implemented for translucency!
		half LineIntegral = 0;
		// Compute the fog volume contribution from the integral.
		return ComputeFogContribution(LineIntegral);
	}
#endif

/** Entry point for the base pass vertex shader. */
void Main(
	FVertexFactoryInput Input,
#if STATICLIGHTING_VERTEXMASK
	float LightMask : BLENDWEIGHT0,
#endif
	out FVertexFactoryInterpolants FactoryInterpolants,
	out FBasePassInterpolants BasePassInterpolants,
	out float4 Position : POSITION
	)
{
	FVertexFactoryIntermediates VFIntermediates = GetVertexFactoryIntermediates(Input);
	float4 WorldPosition = VertexFactoryGetWorldPosition(Input, VFIntermediates);

#if TERRAIN_VERTEX_FACTORY
	// Don't calculate TangentBasis here for terrain, as doing so causes self-z-fighting on xbox 360, regardless of any ISOLATE usage
	FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, WorldPosition.xyz, float3x3(float3(0,0,0),float3(0,0,0),float3(0,0,0)));
#else
	float3x3 TangentBasis = VertexFactoryGetTangentBasis(Input, VFIntermediates);
	FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, WorldPosition.xyz, TangentBasis);
#endif
	// Isolate instructions used for world position offset on xbox 360, 
	// As these cause the optimizer to generate different position calculating instructions in each pass, resulting in self-z-fighting.
	// This is only necessary for shaders used in passes that have depth testing enabled.
	ISOLATE
	{
		WorldPosition.xyz += GetMaterialWorldPositionOffset(VertexParameters);
	}

	Position = MulMatrix(ViewProjectionMatrix,WorldPosition);
	FactoryInterpolants = VertexFactoryGetInterpolants(Input, VFIntermediates);
#if WORLD_POS
	BasePassInterpolants.PixelPosition = WorldPosition;
#else
	BasePassInterpolants.PixelPosition = Position;
#endif

#if TERRAIN_VERTEX_FACTORY
	float3x3 TangentBasis = VertexFactoryGetTangentBasis(Input, VFIntermediates);
#endif

	BasePassInterpolants.CameraVectorOrVertexColor = 1;
#if PER_PIXEL_CAMERA_VECTOR
	BasePassInterpolants.CameraVectorOrVertexColor = VertexFactoryGetVertexColor(Input, VFIntermediates);
#else
	BasePassInterpolants.CameraVectorOrVertexColor.xyz = VertexFactoryWorldToTangentSpace(Input,VFIntermediates,TangentBasis,CameraPosition.xyz - WorldPosition.xyz * CameraPosition.w);
#endif

	#if TANGENT_LIGHT_VECTOR_DEFINED
		BasePassInterpolants.TangentLightVector = 0;
	#endif

	#if ENABLE_MULTITYPE_BASEPASS_LIGHT && XBOX
		// Isolate instructions used for the base pass dynamic light on xbox, 
		// As these cause the optimizer to generate different position calculating instructions for skinned meshes between the depth only and base passes.
		ISOLATE
	#endif
	{
		#if ENABLE_DIRECTIONAL_LIGHT
			BasePassInterpolants.TangentLightVector.xyz = VertexFactoryWorldToTangentSpace(Input,VFIntermediates,TangentBasis,LightDirectionAndbDirectional.xyz);
		#elif ENABLE_MULTITYPE_BASEPASS_LIGHT
			// Choose the vector from the vertex to the light for point and spot lights, and the passed in light direction for directional lights
			float3 EffectiveLightVector = (LightPositionAndInvRadius.xyz - WorldPosition.xyz) * (1.0f - LightDirectionAndbDirectional.w) + LightDirectionAndbDirectional.xyz * LightDirectionAndbDirectional.w;
			BasePassInterpolants.TangentLightVector.xyz = VertexFactoryWorldToTangentSpace(Input, VFIntermediates, TangentBasis, EffectiveLightVector);
			BasePassInterpolants.WorldLightVector = (LightPositionAndInvRadius.xyz - WorldPosition.xyz) * LightPositionAndInvRadius.w;
		#endif

		#if STATICLIGHTING_VERTEXMASK && (ENABLE_DIRECTIONAL_LIGHT || ENABLE_MULTITYPE_BASEPASS_LIGHT)
			BasePassInterpolants.TangentLightVector.w = LightMask;
		#endif
	}

	#if !MATERIAL_LIGHTINGMODEL_UNLIT
		// Calculate sky vector
		BasePassInterpolants.SkyVector.xyz = VertexFactoryWorldToTangentSpace(Input, VFIntermediates, TangentBasis,float3(0,0,1));
	#endif
	
	// Calculate DoF blur amount needed for translucency
	#if ENABLE_TRANSLUCENCY_DOF
		BasePassInterpolants.TranslucencyDoFBlur = 1.f - CalcUnfocusedPercent(Position.z);
	#endif

	// Calculate the fog needed for translucency
	#if NEEDS_BASEPASS_FOGGING
		float4 Fog = GetCombinedFog(
			CalculateVertexHeightFog(WorldPosition.xyz, CameraPosition),
			GetFogDensity(WorldPosition)
			);

			BasePassInterpolants.VertexFog = Fog;
	#endif

	#if NEEDS_VERTEX_LIGHTMAP
		float4 InLightMapA;
		float4 InLightMapB;
		VertexFactoryGetVertexLightMap(Input,InLightMapA,InLightMapB);
		BasePassInterpolants.LightMapA = pow( InLightMapA FCOLOR_COMPONENT_SWIZZLE .rgb, 2.2 ) * LightMapScale[0].rgb;
		BasePassInterpolants.LightMapB = pow( InLightMapB FCOLOR_COMPONENT_SWIZZLE .rgb, 2.2 ) * LightMapScale[1].rgb;

	#elif SIMPLE_VERTEX_LIGHTMAP
		float4 InLightMapA;
		VertexFactoryGetSimpleVertexLightMap(Input,InLightMapA);
		BasePassInterpolants.LightMapA = pow( InLightMapA FCOLOR_COMPONENT_SWIZZLE .rgb, 2.2 ) * LightMapScale[0].rgb;
	#endif
}
