/*=============================================================================
	MotionBlurCommon.usf: Common helper functionality for motion blur.
	Copyright 1998-2010 Epic Games, Inc. All Rights Reserved.
=============================================================================*/

#ifndef NUM_SAMPLES
#define NUM_SAMPLES 5
#endif

float4 RenderTargetClampParameter;

float2 ClampUV(float2 UV)
{
	// Clamp the sampling UVs to avoid sampling from outside viewport region
	// (hence render target size can be bigger than viewport region)
	UV.x = clamp(UV.x, RenderTargetClampParameter.x, RenderTargetClampParameter.z);
	UV.y = clamp(UV.y, RenderTargetClampParameter.y, RenderTargetClampParameter.w);
	return UV;
}


#if XBOX
// XY used for ScaleAndBias: { 3*SCENE_COLOR_BIAS_FACTOR, -2, 0, 0 }
//
float4 MotionBlurMaskScaleAndBias;

// AlphaValue can have four different values (0/3, 1/3, 2/3, 3/3).
// The MotionBlurMask bit is set when AlphaValue is 1/3 or 3/3, and this function
// returns 1.0 if the bit is set, otherwise 0.0.
half GetMotionBlurMask( half AlphaValue )
{
//	half MotionBlurMask = abs( AlphaValue*3.0f - 2.0f );
	half MotionBlurMask = abs( AlphaValue*MotionBlurMaskScaleAndBias.x + MotionBlurMaskScaleAndBias.y );
	return (AlphaValue < 0.001f) ? 0.0f : MotionBlurMask;
}
half4 GetMotionBlurMask4( half Alpha1, half Alpha2, half Alpha3, half Alpha4 )
{
//  half MotionBlurMask = abs( AlphaValue*3.0f - 2.0f );
    half4 AlphaValues = half4( Alpha1, Alpha2, Alpha3, Alpha4 );
    half4 MotionBlurMask = abs( AlphaValues*MotionBlurMaskScaleAndBias.x + MotionBlurMaskScaleAndBias.y );
    MotionBlurMask = (AlphaValues < 0.001f) ? 0.0f : MotionBlurMask;
    return MotionBlurMask;
}
#endif

sampler2D	VelocityBuffer;
sampler2D	LowResSceneBuffer;
float4x4	ScreenToWorld;
float4x4	PrevViewProjMatrix;

//{ 0.5f, -0.5f, MAX_PIXELVELOCITY, MAX_PIXELVELOCITY }
float4 StaticVelocityParameters = { 0.5f, -0.5f, 16.0f/1280.0f, 16.0f/720.0f };
// { 2.0f*MAX_PIXELVELOCITY, -2.0f*MAX_PIXELVELOCITY, 2.0f*MAX_TRANSLUCENT_PIXELVELOCITY, -2.0f*MAX_TRANSLUCENT_PIXELVELOCITY }
float4 DynamicVelocityParameters = { 2.0f*16.0f/1280.0f, -2.0f*16.0f/720.0f, -64.0f/1280.0f, 64.0f/720.0f };

// offsets/weights when sampling using opaque motion based velocity
float StepOffsetsOpaque[NUM_SAMPLES] = {0, 1, 2, -1, -2};
float StepWeightsOpaque[NUM_SAMPLES] = {2.0/10.0, 2/10.0, 2.0/10.0, 2.0/10.0, 2.0/10.0};
// offsets/weights when sampling using translucent non-motion based velocity
float StepOffsetsTranslucent[NUM_SAMPLES] = {0, 1, 2, 3, 4};
float StepWeightsTranslucent[NUM_SAMPLES] = {1.0/5.0, 1.0/5.0, 1.0/5.0, 1.0/5.0, 1.5/10.0};

// return 0..1, 0=no motionblur
half MotionBlur( half3 SceneColor, float4 ScreenPosition, uniform bool UseHalfResolutionInput, out half3 OutColor : COLOR0 )
{
	half2 PixelVelocity = 0;	
	half MaxMotionBlurMask = 0;
    half MaxNeighborMask = 0;

	float2 UV = ScreenPosition.zw;

	// scaling of step offset size for each sample
	float StepOffsets[NUM_SAMPLES] = StepOffsetsOpaque;
	// weight for contribution from each sample
	float StepWeights[NUM_SAMPLES] = StepWeightsOpaque;
	// texture samples
	half3 StepSamples[NUM_SAMPLES];

	// NOTE: Maximum value for an fp16 depth-value is 65504.
	half4	DynamicVelocity	= tex2D( VelocityBuffer, UV );	

	// 0.0f (-1.0f after scale-and-bias) in the velocity buffer has special meaning. It means "use static background velocity".
	// Velocity rendering clamps to 1/255, so every pixel it touches will fail this check.

	bool SelectorOpaque =  DynamicVelocity.x + DynamicVelocity.y > 0;
	half SelectorTranslucent = DynamicVelocity.z;
	if ( SelectorTranslucent > 0 )
	{
		// Scale dynamic velocity from [-1,+1] to [-MAX_TRANSLUCENT_PIXELVELOCITY,+MAX_TRANSLUCENT_PIXELVELOCITY] pixels
		half2 NominalVelocity	= DynamicVelocity.zw * 2.0f - 1.0f;
		PixelVelocity			= NominalVelocity * DynamicVelocityParameters.zw;
		// override offsets/weights to use translucent version
		StepOffsets = StepOffsetsTranslucent;
		StepWeights = StepWeightsTranslucent;
#if XBOX
		// disable masking of characters 
		MaxMotionBlurMask = 100;
		MaxNeighborMask = 100;
#endif
	}
	if ( SelectorOpaque )
	{
		// Scale from [0,1] to [-1,+1]
		half2 NominalVelocity	= DynamicVelocity.xy * 2.0f - 1.0f;
		// Scale dynamic velocity from [-1,+1] to [-MAX_VELOCITY,+MAX_VELOCITY] pixels
		half2 MotionPixelVelocity	= NominalVelocity * DynamicVelocityParameters.xy;

		// Accumulate total pixel velocity
		PixelVelocity += MotionPixelVelocity;
	}
	else
	{
		half	CurrDepth			= min( CalcSceneDepth( UV ), 65504);
		if ( CurrDepth < 14.0f )
			CurrDepth = 65504.0f;
		float4	ProjectedPosition	= float4( ScreenPosition.xy * CurrDepth, CurrDepth, 1 );
		float4	PrevScreenPosition	= MulMatrix( PrevViewProjMatrix, ProjectedPosition );
		PrevScreenPosition.xy		/= PrevScreenPosition.w;

		// Scale static velocity from projection space to texel space and clamp to +/- MAX_VELOCITY pixels.
		half2	StaticVelocity	= ScreenPosition.xy - PrevScreenPosition.xy;

		half2 MotionPixelVelocity = StaticVelocity * StaticVelocityParameters.xy;

		// clamp motion vector in a ellipse from -StaticVelocityParameters.zw to StaticVelocityParameters.zw
		{
			MotionPixelVelocity /= StaticVelocityParameters.zw;

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

			MotionPixelVelocity *= StaticVelocityParameters.zw;
		}
 
		// Accumulate total pixel velocity
		PixelVelocity += MotionPixelVelocity;
	}

	// Average NUM_SAMPLES screen colors along the velocity vector.
	half2 Step = PixelVelocity / NUM_SAMPLES;
	StepSamples[0] = SceneColor;
	UNROLL 
	for(int i=1; i<NUM_SAMPLES; i++)
	{
		if(UseHalfResolutionInput)
		{
			// sample motion blur samples in lower resolution
			StepSamples[i] = tex2D( LowResSceneBuffer, ClampUV(UV + Step*StepOffsets[i]) ).rgb;
		}
		else
		{
			StepSamples[i] = tex2D( SceneColorTexture, ClampUV(UV + Step*StepOffsets[i]) ).rgb;
		}
	}

	OutColor = 0;	

	UNROLL 
	for(int j=0; j<NUM_SAMPLES; j++)
	{
		OutColor += StepSamples[j]*StepWeights[j];
	}

	// compute motion strength to fade out motion blur if there is little movement
	// to see the full resolution source image, tweaked value
	float MotionStrength = saturate(dot(PixelVelocity, PixelVelocity) * 6000.0f);

	// 0..1 0:0% motionblur , 1:100% motion blur
	return MotionStrength;
}
