#ifndef VR_TRIPLANAR_BLEND_FXC
#define VR_TRIPLANAR_BLEND_FXC

#include "common_samplers.fxc"

//=========================================================================================================================
//
// Triplanar Material
//
//=========================================================================================================================

float g_flTriplanarScale < Default( 1.0 ); Range( 0.0, 2048.0 ); UiGroup( "Triplanar/30" ); >;

//
// Packed textures
//
CreateInputTexture2D( TextureNormalB, Linear, 8, "NormalizeNormals", "_normal", "Triplanar Material,20/20", Default3( 0.5, 0.5, 1.0 ) );
CreateInputTexture2D( TextureRoughnessB, Linear, 8, "Inverse", "_rough", "Triplanar Material,20/30", Default3( 0.5, 0.5, 0.5 ) );
CreateInputTexture2D( TextureAmbientOcclusionB, Linear, 8, "", "_ao", "Triplanar Material,20/40", Default3( 1.0, 1.0, 1.0 ) );
CreateInputTexture2D( TextureColorB, Srgb, 8, "", "_color", "Triplanar Material,20/10", Default3( 1.0, 1.0, 1.0 ) );
CreateInputTexture2D( TextureTriplanarMask, Linear, 8, "", "_mask", "Triplanar Material,20/50", Default3( 1.0, 1.0, 1.0 ) );
float g_flMetalnessB < Default( 0.0 ); Range( 0.0, 1.0 ); UiGroup( "Triplanar Material,20/60" ); >;

Texture2D g_tColorB < Channel( RGB, Box( TextureColorB ), Srgb ); Channel( A, Box( TextureAmbientOcclusionB ), Linear ); OutputFormat( BC7 ); SrgbRead( true ); >;
Texture2D g_tNormalB < Channel( RGBA, HemiOctIsoRoughness_RG_B( TextureNormalB, TextureRoughnessB ), Linear ); OutputFormat( BC7 ); SrgbRead( false ); >;
Texture2D g_tTriplanarMask < Channel( R, Box( TextureTriplanarMask ), Linear ); OutputFormat( ATI1N ); SrgbRead( false ); >;

//=========================================================================================================================

void CalcTriplanarUVsAndBlendFactors( out float2 vUV_X, out float2 vUV_Y, out float2 vUV_Z, out float3 vBlendFactor, float3 vPositionWs,  float3 vNormalWs, float flBlendPower = 16.f )
{
	//
	// Project UVs
	//
	float3 vTriplanarUVW = vPositionWs.xyz / ( g_flTriplanarScale );

	vUV_X = -vTriplanarUVW.yz;
	vUV_Y = vTriplanarUVW.xz;
	vUV_Z = vTriplanarUVW.xy;

	//
	// Calc Blend Factor
	//
	vBlendFactor = pow( abs( vNormalWs ), flBlendPower );
	vBlendFactor /= dot( vBlendFactor, float3( 1, 1, 1 ) );
}

//=========================================================================================================================

void TriPlanarBlendColorNormalRoughness( float2 vUV_X, float2 vUV_Y, float2 vUV_Z, float3 vBlendFactor, float3 vNormalWs, out float3 vNormalWs_Out, out float4 vColorAO_Out, out float2 vRoughness_Out, float flBlendPower, bool bFlipY )
{
	float4 vColorTexelB_X = Tex2DS( g_tColorB, g_sTrilinearWrap, vUV_X );
	float4 vColorTexelB_Y = Tex2DS( g_tColorB, g_sTrilinearWrap, vUV_Y );
	float4 vColorTexelB_Z = Tex2DS( g_tColorB, g_sTrilinearWrap, vUV_Z );

	float4 vNormalTexelB_X = Tex2DS( g_tNormalB, g_sTrilinearWrap, vUV_X );
	float4 vNormalTexelB_Y = Tex2DS( g_tNormalB, g_sTrilinearWrap, vUV_Y );
	float4 vNormalTexelB_Z = Tex2DS( g_tNormalB, g_sTrilinearWrap, vUV_Z );


	vColorAO_Out = vColorTexelB_X * vBlendFactor.x +
				   vColorTexelB_Y * vBlendFactor.y +
				   vColorTexelB_Z * vBlendFactor.z ;

	vRoughness_Out = vNormalTexelB_X.ba * vBlendFactor.x +
					 vNormalTexelB_Y.ba * vBlendFactor.y +
					 vNormalTexelB_Z.ba * vBlendFactor.z ;


	float3 vNormalB_XTs = DecodeHemiOctahedronNormal( vNormalTexelB_X.rg );
	float3 vNormalB_YTs = DecodeHemiOctahedronNormal( vNormalTexelB_Y.rg );
	float3 vNormalB_ZTs = DecodeHemiOctahedronNormal( vNormalTexelB_Z.rg );

	//
	// Swizzle tangent normals into world space
	//
	float3 vNormalX = vNormalB_XTs.xzy * float3( -1.0, 0, 1.0 );
	float3 vNormalY = vNormalB_YTs.xzy * float3( 1.0, 0, -1.0 );
	float3 vNormalZ = vNormalB_ZTs.xyz * float3( 1.0, -1.0, 0 );
	
	float3 vBlendedNormal = vNormalX.xyz * vBlendFactor.x +
							vNormalY.xyz * vBlendFactor.y +
							vNormalZ.xyz * vBlendFactor.z;

	//
	// Triblend normals and add to world normal
	//
	vNormalWs_Out = normalize( vNormalWs.xyz + vBlendedNormal.xyz );
}

//-----------------------------------------------------------------------------------------------------------------

void TriPlanarBlend( float3 vPositionWs, float3 vNormalWs, out float3 vNormalWs_Out, out float4 vColorAO_Out, out float2 vRoughness_Out, float flBlendPower = 16.f, bool bFlipY = true )
{
	float2 vUV_X;
	float2 vUV_Y;
	float2 vUV_Z;
	float3 vBlendFactor;
	
	CalcTriplanarUVsAndBlendFactors( vUV_X, vUV_Y, vUV_Z, vBlendFactor, vPositionWs, vNormalWs, flBlendPower );

	TriPlanarBlendColorNormalRoughness( vUV_X, vUV_Y, vUV_Z, vBlendFactor, vNormalWs, vNormalWs_Out, vColorAO_Out, vRoughness_Out, flBlendPower, bFlipY );
}

//=========================================================================================================================

void TriPlanarBlendWithMask( float3 vPositionWs, float3 vNormalWs, out float3 vNormalWs_Out, out float4 vColorAO_Out, out float2 vRoughness_Out, out float flMask_Out, float flBlendPower = 16.f, bool bFlipY = true )
{
	float2 vUV_X;
	float2 vUV_Y;
	float2 vUV_Z;
	float3 vBlendFactor;

	CalcTriplanarUVsAndBlendFactors( vUV_X, vUV_Y, vUV_Z, vBlendFactor, vPositionWs, vNormalWs, flBlendPower );

	float vMaskTexel_X = Tex2DS( g_tTriplanarMask, g_sTrilinearWrap, vUV_X ).r;
	float vMaskTexel_Y = Tex2DS( g_tTriplanarMask, g_sTrilinearWrap, vUV_Y ).r;
	float vMaskTexel_Z = Tex2DS( g_tTriplanarMask, g_sTrilinearWrap, vUV_Z ).r;

	flMask_Out = vMaskTexel_X * vBlendFactor.x +
				 vMaskTexel_Y * vBlendFactor.y +
				 vMaskTexel_Z * vBlendFactor.z ;

	TriPlanarBlendColorNormalRoughness( vUV_X, vUV_Y, vUV_Z, vBlendFactor, vNormalWs, vNormalWs_Out, vColorAO_Out, vRoughness_Out, flBlendPower, bFlipY );
}
//=========================================================================================================================

#endif

