/*=============================================================================
	MotionVelocityShader.usf: Calculates velocity vectors.
	Copyright 1998-2010 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

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


/*=============================================================================
 * Vertex Shader
 *============================================================================*/

float4x4 PrevViewProjectionMatrix;
float4 StretchTimeScale = { 1.0f, 0.0f, 0.0f, 0.0f };

struct FVertexOutput
{
	// xy for object, zw for camera
    float4 ScreenVelocity : TEXCOORD6;
	FVertexFactoryInterpolants FactoryInterpolants;	
};

void MainVertexShader( FVertexFactoryInput Input, out FVertexOutput Output, out float4 Position : POSITION)
{
	FVertexFactoryIntermediates VFIntermediates = GetVertexFactoryIntermediates(Input);
	float4 WorldPosition		= VertexFactoryGetWorldPosition( Input, VFIntermediates );
	float3x3 TangentBasis		= VertexFactoryGetTangentBasis( Input, VFIntermediates );

	FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, WorldPosition.xyz, TangentBasis);
	// 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);
	}

	Output.FactoryInterpolants	= VertexFactoryGetInterpolants( Input, VFIntermediates );
	float4 PrevWorldPosition	= VertexFactoryGetPreviousWorldPosition( Input, VFIntermediates );	

	VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, PrevWorldPosition.xyz, TangentBasis);
	PrevWorldPosition.xyz += GetMaterialWorldPositionOffset(VertexParameters);

//@todo - this causes primitives stretching to infinity
#if 0
	// Calculate world-space normal vector
	float3 WorldNormal			= VertexFactoryGetWorldNormal( Input );
	// Calculate world-space velocity vector in units/sec.
	float3 WorldVelocity = (WorldPosition.xyz - PrevWorldPosition.xyz);
	// Is the normal pointing backwards?
 	if ( dot(WorldNormal, WorldVelocity) < 0 )
 	{
 		// Stretch the polygon back and let the velocity interpolate between actual velocity and 0.
 		Position				= MulMatrix( ViewProjectionMatrix, WorldPosition - WorldVelocity.xyzz*StretchTimeScale.xxxw );
 		Output.PrevPosition		= Position;
		Output.CurrPosition		= Position;
 	}
 	else
#endif
	{
		// Note: PrevWorldPosition is missing +=PreViewTranslation.xyz and PrevViewProjectionMatrix already compensates for that already
		float4 ScreenPosition			= MulMatrix(     ViewProjectionMatrix, WorldPosition );
		float4 PrevScreenPositionObj	= MulMatrix(     ViewProjectionMatrix, float4(PrevWorldPosition.xyz + PreViewTranslation.xyz, 1));
		float4 PrevScreenPositionCam	= MulMatrix( PrevViewProjectionMatrix, float4(WorldPosition.xyz - PreViewTranslation.xyz, 1));

		Position = ScreenPosition;

		float2 ScrPos = ScreenPosition.xy / ScreenPosition.w;

		// object motion vector
		Output.ScreenVelocity.xy = ScrPos - PrevScreenPositionObj.xy / PrevScreenPositionObj.w;
		// camera motion vector
		Output.ScreenVelocity.zw = ScrPos - PrevScreenPositionCam.xy / PrevScreenPositionCam.w;
	}

	// Move all geometry a little bit towards the camera.
	Position.z += StretchTimeScale.y * Position.w;
}


/*=============================================================================
 * Pixel Shader
 *============================================================================*/

// This parameter brings a [-2,+2] velocity vector into [0,1] range,
// where 0 is mapped to -MaxVelocity, and 1 is mapped to +MaxVelocity.
// These values depend on screen resolution and blur distance, so they
// should be adjusted from CPU.
// Initialized here for a 16-pixel blur distance on a 640,480 resolution.
// XY = Multiplier, ZW = Offset

float4 VelocityScaleOffset = { 0.25f*640.0f/16.0f, 0.25f*480.0f/16.0f, 0.5f, 0.5f };
float4 IndividualVelocityScale = { 1.0f, 1.0f, 1.0f/255.0f, 1.0f };

void MainPixelShader(
	in FVertexOutput Input,
	OPTIONAL_FacingSign
	OPTIONAL_PixelShaderScreenPosition
	out float4 OutColor : COLOR0
	)
{
	// Manual clipping here (alpha-test, etc)
	FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters( Input.FactoryInterpolants );
	CalcMaterialParameters( MaterialParameters, FacingSign, float4(0,0,1,0), float4(0,0,.00001f,1));
	GetMaterialClipping( MaterialParameters, PixelShaderScreenPosition.xy );

	// xy for object, zw for camera
	half4 PackedVelocity = Input.ScreenVelocity;

	// Scale by opacity
	PackedVelocity.xy *= GetMaterialOpacity( MaterialParameters );

	// Scale by UPrimitiveComponent::MotionBlurScale and resolution
	PackedVelocity *= IndividualVelocityScale;

	// final = object + camera
	half2 Velocity = PackedVelocity.xy + PackedVelocity.zw;

	// clamp motion vector in a circle from -1 to 1
	{
		half Len = dot(Velocity, Velocity);
		FLATTEN if(Len > 1)
		{
			Velocity *= rsqrt(Len);
		}
	}

	// Map [-1,1] to [0,1].
	Velocity	= Velocity*0.5f + 0.5f;

	OutColor = float4(Velocity, 0, 0);
}
