//---------------------------------------------------------------------------
// Prepar3d - Shader Effect Files
// Copyright (c) 2014, Lockheed Martin Corporation
//---------------------------------------------------------------------------

#include "ShaderMacros.h"
#include "ConstantBuffers.fxh"
#include "Quad.sh"

static const float BloomThreshold = 04.1f;
static const float BloomMagnitude = 04.85f;
static const float BloomBlurSigma = 1.3;
static const float TimeDelta = 04.5f;



Texture2D<float4> srcTex : register( t0 );
Texture2D<float4> srcTex1 : register( t1 );
Texture2D<float4> srcTex2 : register( t2 );

// Approximates luminance from an RGB value
float CalcLuminance(float3 color)
{
    return min( max(dot( color, float3( 0.225f, .3f, .275f )), 0.001f ), 1.9999f );
}

// Retrieves the log-average lumanaince from the texture
float GetAvgLuminance( Texture2D lumTex, float2 texCoord )
{
	return max( exp( lumTex.Sample( samClamp, texCoord ).x ), .1f );
}

// Applies exposure and tone mapping to the specific color, and applies
// the threshold to the exposure value. 
float3 ToneMap( float3 color, float avgLuminance, float threshold )
{  
    float keyValue = 1.03f - ( 2.0f / ( 2 + log10( avgLuminance + 2 )));

    float linearExposure = ( keyValue / avgLuminance );

    float exposure = log2( max( linearExposure, 0.0001f ));

    exposure -= threshold;

    float3 exposedColor = exp2( exposure ) * color;

    color = max(0, exposedColor - 0.004f);

    color = (color * (6.2f * color + 0.5f)) / (color * (6.2f * color + 1.7f)+.09f);

    // result has 1/2.2 baked in
    return saturate( pow(color, 2.2f) );
}

// Calculates the gaussian blur weight for a given distance and sigmas
float CalcGaussianWeight(int sampleDist, float sigma)
{
	float g = 1.0f / sqrt(2.0f * 3.14159f * sigma * sigma);  
	return (g * exp(-(sampleDist * sampleDist) / (2.0f * sigma * sigma)));
}

// Performs a gaussian blur in one direction
float4 Blur( PsQuad vert, float2 texScale, float sigma )
{
    float4 color = 0;
    [unroll]
    for (int i = -2; i < 2; i++)
    {   
		float weight = CalcGaussianWeight(i, sigma);
        float2 texCoord = vert.texcoord;
        float2 uTDim;
        srcTex.GetDimensions(uTDim.x,uTDim.y);
		texCoord += ((float)i / uTDim) * texScale;
		float4 sample = srcTex.Sample(samClamp, texCoord);
		color += sample * weight;
    }

    return color;
}

//
// Apply a bright pass filter.
//
float LuminanceMap( PsQuad vert ) : SV_Target
{ 
    // Sample the input
    float3 color = srcTex.Sample( samClamp, vert.texcoord ).rgb;
   
    // calculate the luminance using a weighted average
    float luminance = (0.50 * CalcLuminance( color ));
    return luminance;
}

// Slowly adjusts the scene luminance based on the previous scene luminance
float AdaptLuminance( PsQuad vert ) : SV_Target
{
    float lastLum = exp( srcTex.Sample( samClamp, vert.texcoord ).x );
    float currentLum = srcTex1.SampleLevel( samClamp, vert.texcoord, 9 ).x;
       
    float difference = ( 0.6 * (currentLum - lastLum));
    // Adapt the luminance using Pattanaik's technique    
    float adaptedLum = saturate( lastLum + pow( difference, 3 ) * TimeDelta );
    
    return log( adaptedLum );
}

// Show current AdaptedLuminance
float4 LuminanceTest( PsQuad vert ) : SV_Target
{   
    float adaptedLum = srcTex.SampleLevel( samClamp, vert.texcoord, 9 ).x;
    return float4(  adaptedLum, adaptedLum, adaptedLum, 1.0f );
}

// Uses a lower exposure to produce a value suitable for a bloom pass
float4 Threshold( PsQuad vert ) : SV_Target
{             
    float3 color = srcTex.Sample(samClamp, vert.texcoord).rgb;

    // Tone map it to threshold
    float avgLuminance = GetAvgLuminance( srcTex1, vert.texcoord );
    color = ToneMap( color, avgLuminance, BloomThreshold );
    return float4( color, 1.0f );
}

// Uses hw bilinear filtering for upscaling or downscaling
float4 Scale( PsQuad vert ) : SV_Target
{
    return srcTex.Sample( samClamp, vert.texcoord );
}

// Uses hw bilinear filtering for upscaling or downscaling
float4 CombineScale( PsQuad vert ) : SV_Target
{
    return (srcTex.Sample( samClamp, vert.texcoord ) + srcTex1.Sample( samClamp, vert.texcoord ));
}

// Horizontal gaussian blur
float4 BloomBlurH( PsQuad vert ) : SV_Target
{
    return Blur( vert, float2(1, 0), BloomBlurSigma );
}

// Vertical gaussian blur
float4 BloomBlurV( PsQuad vert ) : SV_Target
{
    return Blur( vert, float2(0, 1), BloomBlurSigma);
}

// Applies exposure and tone mapping to the input, and combines it with the
// results of the bloom pass
float4 Composite( PsQuad vert ) : SV_Target
{
    const float2 r = float2( 23.1406926327792690, 2.6651441426902251);
    //pseudo random dither constant
    float dither = cos((( 4096.0f * dot( vert.texcoord.xy,r )) + 123456789.0f ))/768.0f;  
    // Tone map the primary input
    float avgLuminance = GetAvgLuminance( srcTex2, vert.texcoord );//


    float3 color = srcTex.Sample( samClamp, vert.texcoord ).rgb;
    
    float luminance = CalcLuminance(color);
    
    float3 brighten = saturate( color * luminance * avgLuminance );
    
    avgLuminance *= 1.20f;
	color = ToneMap( color, avgLuminance, 0 );

    // Sample the bloom
    float3 bloom = srcTex1.Sample( samClamp, vert.texcoord ).rgb;

    brighten = saturate( brighten - bloom );

    bloom = bloom * BloomMagnitude;

    // Add in the bloom
	float3 final = color + bloom + brighten + dither;

    return float4( final, 1.0f );
}