// File generated by Flax Materials Editor
// Version: @0

#define MATERIAL 1
@3

#include "./Flax/Common.hlsl"
#include "./Flax/MaterialCommon.hlsl"
#include "./Flax/GBufferCommon.hlsl"
@7
// Primary constant buffer (with additional material parameters)
META_CB_BEGIN(0, Data)
float4x4 ViewProjectionMatrix;
float4x4 WorldMatrix;
float4x4 ViewMatrix;
float4x4 InvWorld;
float4x4 SVPositionToWorld;
float3 ViewPos;
float ViewFar;
float3 ViewDir;
float TimeParam;
float4 ViewInfo;
float4 ScreenSize;
@1META_CB_END

// Use depth buffer for per-pixel decal layering
Texture2D DepthBuffer : register(t0);

// Material shader resources
@2
// Material properties generation input
struct MaterialInput
{
	float3 WorldPosition;
	float TwoSidedSign;
	float2 TexCoord;
	float3x3 TBN;
	float4 SvPosition;
	float3 PreSkinnedPosition;
	float3 PreSkinnedNormal;
};

// Transforms a vector from tangent space to world space
float3 TransformTangentVectorToWorld(MaterialInput input, float3 tangentVector)
{
	return mul(tangentVector, input.TBN);
}

// Transforms a vector from world space to tangent space
float3 TransformWorldVectorToTangent(MaterialInput input, float3 worldVector)
{
	return mul(input.TBN, worldVector);
}

// Transforms a vector from world space to view space
float3 TransformWorldVectorToView(MaterialInput input, float3 worldVector)
{
	return mul(worldVector, (float3x3)ViewMatrix);
}

// Transforms a vector from view space to world space
float3 TransformViewVectorToWorld(MaterialInput input, float3 viewVector)
{
	return mul((float3x3)ViewMatrix, viewVector);
}

// Transforms a vector from local space to world space
float3 TransformLocalVectorToWorld(MaterialInput input, float3 localVector)
{
	float3x3 localToWorld = (float3x3)WorldMatrix;
	return mul(localVector, localToWorld);
}

// Transforms a vector from local space to world space
float3 TransformWorldVectorToLocal(MaterialInput input, float3 worldVector)
{
	float3x3 localToWorld = (float3x3)WorldMatrix;
	return mul(localToWorld, worldVector);
}

// Gets the current object position (supports instancing)
float3 GetObjectPosition(MaterialInput input)
{
	return WorldMatrix[3].xyz;
}

// Gets the current object size
float3 GetObjectSize(MaterialInput input)
{
	return float3(1, 1, 1);
}

// Get the current object random value supports instancing)
float GetPerInstanceRandom(MaterialInput input)
{
	return 0;
}

// Get the current object LOD transition dither factor (supports instancing)
float GetLODDitherFactor(MaterialInput input)
{
	return 0;
}

// Gets the interpolated vertex color (in linear space)
float4 GetVertexColor(MaterialInput input)
{
	return 1;
}

// Get material properties function (for pixel shader)
Material GetMaterialPS(MaterialInput input)
{
@4
}

// Programmatically set the line number after all the material inputs which have a variable number of line endings
// This allows shader error line numbers after this point to be the same regardless of which material is being compiled
#line 1000

// Input macro specified by the material: DECAL_BLEND_MODE

#define DECAL_BLEND_MODE_TRANSLUCENT 0
#define DECAL_BLEND_MODE_STAIN       1
#define DECAL_BLEND_MODE_NORMAL      2
#define DECAL_BLEND_MODE_EMISSIVE    3

// Vertex Shader function for decals rendering
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true)
void VS_Decal(in float3 Position : POSITION0, out float4 SvPosition : SV_Position)
{
	// Compute world space vertex position
	float3 worldPosition = mul(float4(Position.xyz, 1), WorldMatrix).xyz;

	// Compute clip space position
	SvPosition = mul(float4(worldPosition.xyz, 1), ViewProjectionMatrix);
}

// Pixel Shader function for decals rendering
META_PS(true, FEATURE_LEVEL_ES2)
void PS_Decal(
	in float4 SvPosition : SV_Position
	, out float4 Out0 : SV_Target0
#if DECAL_BLEND_MODE == DECAL_BLEND_MODE_TRANSLUCENT
	, out float4 Out1 : SV_Target1
#if USE_NORMAL || USE_EMISSIVE
	, out float4 Out2 : SV_Target2
#endif
#if USE_NORMAL && USE_EMISSIVE
	, out float4 Out3 : SV_Target3
#endif
#endif
	)
{
	float2 screenUV = SvPosition.xy * ScreenSize.zw;
	SvPosition.z = SAMPLE_RT(DepthBuffer, screenUV).r;

	float4 positionHS = mul(float4(SvPosition.xyz, 1), SVPositionToWorld);
	float3 positionWS = positionHS.xyz / positionHS.w;
	float3 positionOS = mul(float4(positionWS, 1), InvWorld).xyz;

	// Clip content outside the decal
	clip(0.5 - abs(positionOS.xyz));

	// By default, map textures using the vectors perpendicular to the projection direction
	float2 decalUVs = positionOS.xz + 0.5f;
	
	// Setup material input
	MaterialInput materialInput = (MaterialInput)0;
	materialInput.WorldPosition = positionWS;
	materialInput.TexCoord = decalUVs;
	materialInput.TwoSidedSign = 1;
	materialInput.SvPosition = SvPosition;
	
	// Build tangent to world transformation matrix
	float3 ddxWp = ddx(positionWS);
	float3 ddyWp = ddy(positionWS);
	materialInput.TBN[0] = normalize(ddyWp);
	materialInput.TBN[1] = normalize(ddxWp);
	materialInput.TBN[2] = normalize(cross(ddxWp, ddyWp));

	// Sample material
	Material material = GetMaterialPS(materialInput);

	// Masking
#if MATERIAL_MASKED
	clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif

	// Set the output
#if DECAL_BLEND_MODE == DECAL_BLEND_MODE_TRANSLUCENT
	// GBuffer0
	Out0 = float4(material.Color, material.Opacity);
	// GBuffer2
	Out1 = float4(material.Roughness, material.Metalness, material.Specular, material.Opacity);
#if USE_EMISSIVE
	// Light Buffer
	Out2 = float4(material.Emissive, material.Opacity);
#if USE_NORMAL
	// GBuffer1
	Out3 = float4(material.WorldNormal * 0.5f + 0.5f, 1);
#endif
#elif USE_NORMAL
	// GBuffer1
	Out2 = float4(material.WorldNormal * 0.5f + 0.5f, 1);
#endif
#elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_STAIN
	Out0 = float4(material.Color, material.Opacity);
#elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_NORMAL
	Out0 = float4(material.WorldNormal * 0.5f + 0.5f, 1);
#elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_EMISSIVE
	Out0 = float4(material.Emissive * material.Opacity, material.Opacity);
#else
	#error "Invalid decal blending mode"
#endif
}
