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

#define MATERIAL 1
#define USE_PER_VIEW_CONSTANTS 1
@3

#include "./Flax/Common.hlsl"
#include "./Flax/MaterialCommon.hlsl"
#include "./Flax/GBufferCommon.hlsl"
#include "./Flax/Matrix.hlsl"
@7
struct SpriteInput
{
	float2 Position : POSITION;
	float2 TexCoord : TEXCOORD;
};

struct RibbonInput
{
	uint Order : TEXCOORD0;
	uint ParticleIndex : TEXCOORD1;
	uint PrevParticleIndex : TEXCOORD2;
	float Distance : TEXCOORD3;
};

// Primary constant buffer (with additional material parameters)
META_CB_BEGIN(0, Data)
float4x4 WorldMatrix;
uint SortedIndicesOffset;
float PerInstanceRandom;
int ParticleStride;
int PositionOffset;
int SpriteSizeOffset;
int SpriteFacingModeOffset;
int SpriteFacingVectorOffset;
int VelocityOffset;
int RotationOffset;
int ScaleOffset;
int ModelFacingModeOffset;
float RibbonUVTilingDistance;
float2 RibbonUVScale;
float2 RibbonUVOffset;
int RibbonWidthOffset;
int RibbonTwistOffset;
int RibbonFacingVectorOffset;
uint RibbonSegmentCount;
float4x4 WorldMatrixInverseTransposed;
@1META_CB_END

// Particles attributes buffer
ByteAddressBuffer ParticlesData : register(t0);

// Sorted particles indices
Buffer<uint> SortedIndices : register(t1);

// Shader resources
@2
// Interpolants passed from the vertex shader
struct VertexOutput
{
	float4 Position          : SV_Position;
	float3 WorldPosition     : TEXCOORD0;
	float2 TexCoord          : TEXCOORD1;
	uint ParticleIndex       : TEXCOORD2;
#if USE_VERTEX_COLOR
	half4 VertexColor        : COLOR;
#endif
	float3x3 TBN             : TEXCOORD3;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
	float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
	nointerpolation float3 InstanceOrigin : TEXCOORD6;
	nointerpolation float InstanceParams : TEXCOORD7; // x-PerInstanceRandom
};

// Interpolants passed to the pixel shader
struct PixelInput
{
	float4 Position          : SV_Position;
	float3 WorldPosition     : TEXCOORD0;
	float2 TexCoord          : TEXCOORD1;
	uint ParticleIndex       : TEXCOORD2;
#if USE_VERTEX_COLOR
	half4 VertexColor        : COLOR;
#endif
	float3x3 TBN             : TEXCOORD3;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
	float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9;
#endif
	nointerpolation float3 InstanceOrigin : TEXCOORD6;
	nointerpolation float InstanceParams : TEXCOORD7; // x-PerInstanceRandom
	bool IsFrontFace         : SV_IsFrontFace;
};

// Material properties generation input
struct MaterialInput
{
	float3 WorldPosition;
	float TwoSidedSign;
	float2 TexCoord;
	uint ParticleIndex;
#if USE_VERTEX_COLOR
	half4 VertexColor;
#endif
	float3x3 TBN;
	float4 SvPosition;
	float3 PreSkinnedPosition;
	float3 PreSkinnedNormal;
	float3 InstanceOrigin;
	float InstanceParams;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
	float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT];
#endif
};

MaterialInput GetMaterialInput(PixelInput input)
{
	MaterialInput result = (MaterialInput)0;
	result.WorldPosition = input.WorldPosition;
	result.TexCoord = input.TexCoord;
	result.ParticleIndex = input.ParticleIndex;
#if USE_VERTEX_COLOR
	result.VertexColor = input.VertexColor;
#endif
	result.TBN = input.TBN;
	result.TwoSidedSign = input.IsFrontFace ? 1.0 : -1.0;
	result.InstanceOrigin = input.InstanceOrigin;
	result.InstanceParams = input.InstanceParams;
	result.SvPosition = input.Position;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
	result.CustomVSToPS = input.CustomVSToPS;
#endif
	return result;
}

// Gets the local to world transform matrix (supports instancing)
#if USE_INSTANCING
#define GetInstanceTransform(input) float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f))
#else
#define GetInstanceTransform(input) WorldMatrix;
#endif

// Removes the scale vector from the local to world transformation matrix (supports instancing)
float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld)
{
	// Extract per axis scales from localToWorld transform
	float scaleX = length(localToWorld[0]);
	float scaleY = length(localToWorld[1]);
	float scaleZ = length(localToWorld[2]);
	float3 invScale = float3(
		scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
		scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
		scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
	localToWorld[0] *= invScale.x;
	localToWorld[1] *= invScale.y;
	localToWorld[2] *= invScale.z;
	return localToWorld;
}

// 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)GetInstanceTransform(input);
	//localToWorld = RemoveScaleFromLocalToWorld(localToWorld);
	return mul(localVector, localToWorld);
}

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

// Gets the current object position (supports instancing)
float3 GetObjectPosition(MaterialInput input)
{
	return input.InstanceOrigin.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 input.InstanceParams;
}

// 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)
{
#if USE_VERTEX_COLOR
	return input.VertexColor;
#else
	return 1;
#endif
}

uint GetParticleUint(uint particleIndex, int offset)
{
	return ParticlesData.Load(particleIndex * ParticleStride + offset);
}

int GetParticleInt(uint particleIndex, int offset)
{
	return asint(ParticlesData.Load(particleIndex * ParticleStride + offset));
}

float GetParticleFloat(uint particleIndex, int offset)
{
	return asfloat(ParticlesData.Load(particleIndex * ParticleStride + offset));
}

float2 GetParticleVec2(uint particleIndex, int offset)
{
	return asfloat(ParticlesData.Load2(particleIndex * ParticleStride + offset));
}

float3 GetParticleVec3(uint particleIndex, int offset)
{
	return asfloat(ParticlesData.Load3(particleIndex * ParticleStride + offset));
}

float4 GetParticleVec4(uint particleIndex, int offset)
{
	return asfloat(ParticlesData.Load4(particleIndex * ParticleStride + offset));
}

float3 TransformParticlePosition(float3 input)
{
	return mul(float4(input, 1.0f), WorldMatrix).xyz;
}

float3 TransformParticleVector(float3 input)
{
	return mul(float4(input, 0.0f), WorldMatrixInverseTransposed).xyz;
}

@8

// Get material properties function (for vertex shader)
Material GetMaterialVS(MaterialInput input)
{
@5
}

// Get material properties function (for domain shader)
Material GetMaterialDS(MaterialInput input)
{
@6
}

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

// Calculates the transform matrix from mesh tangent space to local space
half3x3 CalcTangentToLocal(ModelInput input)
{
	float bitangentSign = input.Tangent.w ? -1.0f : +1.0f;
	float3 normal = input.Normal.xyz * 2.0 - 1.0;
	float3 tangent = input.Tangent.xyz * 2.0 - 1.0;
	float3 bitangent = cross(normal, tangent) * bitangentSign;
	return float3x3(tangent, bitangent, normal);
}

half3x3 CalcTangentToWorld(in float4x4 world, in half3x3 tangentToLocal)
{
	half3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world);
	return mul(tangentToLocal, localToWorld); 
}

float3 GetParticlePosition(uint ParticleIndex)
{
	return TransformParticlePosition(GetParticleVec3(ParticleIndex, PositionOffset));
}

// Vertex Shader function for Sprite Rendering
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(POSITION, 0, R32G32_FLOAT, 0, 0,     PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
VertexOutput VS_Sprite(SpriteInput input, uint particleIndex : SV_InstanceID)
{
	VertexOutput output;

	// Sorted particles mapping
	if (SortedIndicesOffset != 0xFFFFFFFF)
	{
		particleIndex = SortedIndices[SortedIndicesOffset + particleIndex];
	}

	// Read particle data	
	float3 particlePosition = GetParticleVec3(particleIndex, PositionOffset);
	float3 particleRotation = GetParticleVec3(particleIndex, RotationOffset);
	float2 spriteSize = GetParticleVec2(particleIndex, SpriteSizeOffset);
	int spriteFacingMode = SpriteFacingModeOffset != -1 ? GetParticleInt(particleIndex, SpriteFacingModeOffset) : -1;

	float4x4 world = WorldMatrix;
	float3x3 eulerMatrix = EulerMatrix(radians(particleRotation));
	float3x3 viewRot = transpose((float3x3)ViewMatrix);
	float3 position = mul(float4(particlePosition, 1), world).xyz;

	// Orient sprite
	float3 axisX, axisY, axisZ;
	if (spriteFacingMode == 0)
	{
		// Face Camera Position
		axisZ = normalize(ViewPos - position);
		axisX = -normalize(cross(viewRot[1].xyz, axisZ));
		axisY = cross(axisZ, axisX);
	}
	else if (spriteFacingMode == 1)
	{
		// Face Camera Plane
		axisX = viewRot[0].xyz;
		axisY = -viewRot[1].xyz;
		axisZ = viewRot[2].xyz;
	}
	else if (spriteFacingMode == 2)
	{
		// Along Velocity
		float3 velocity = GetParticleVec3(particleIndex, VelocityOffset);
		axisY = normalize(velocity);
		axisZ = ViewPos - position;
		axisX = normalize(cross(axisY, axisZ));
		axisZ = cross(axisX, axisY);
	}
	else if (spriteFacingMode == 3)
	{
		// Custom Facing Vector
		float3 spriteFacingVector = GetParticleVec3(particleIndex, SpriteFacingVectorOffset);
		axisZ = spriteFacingVector;
		axisX = normalize(cross(viewRot[1].xyz, axisZ));
		axisY = cross(axisZ, axisX);
	}
	else if (spriteFacingMode == 4)
	{
		// Fixed Axis
		float3 spriteFacingVector = GetParticleVec3(particleIndex, SpriteFacingVectorOffset);
		axisY = spriteFacingVector;
		axisZ = ViewPos - position;
		axisX = normalize(cross(axisY, axisZ));
		axisZ = cross(axisX, axisY);
	}
	else
	{
		// Default
		axisX = float3(1, 0, 0);
		axisY = float3(0, 1, 0);
		axisZ = float3(0, 0, 1);
	}

	// Compute world space vertex position
	float3 spriteVertexPosition = float3(input.Position.xy * spriteSize, 0);
	spriteVertexPosition = mul(spriteVertexPosition, eulerMatrix);
	spriteVertexPosition = mul(spriteVertexPosition, float3x3(axisX, axisY, axisZ));
	output.WorldPosition = position + spriteVertexPosition;

	// Compute clip space position
	output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix);

	// Pass vertex attributes
	output.TexCoord = input.TexCoord;
	output.ParticleIndex = particleIndex;
#if USE_VERTEX_COLOR
	output.VertexColor = 1;
#endif
	output.InstanceOrigin = world[3].xyz;
	output.InstanceParams = PerInstanceRandom;

	// Calculate tanget space to world space transformation matrix for unit vectors
	half3x3 tangentToLocal = float3x3(axisX, axisY, axisZ);
	half3x3 tangentToWorld = CalcTangentToWorld(world, tangentToLocal);
	output.TBN = tangentToWorld;

	// Get material input params if need to evaluate any material property
#if USE_POSITION_OFFSET || USE_CUSTOM_VERTEX_INTERPOLATORS
	MaterialInput materialInput = (MaterialInput)0;
	materialInput.WorldPosition = output.WorldPosition;
	materialInput.TexCoord = output.TexCoord;
	materialInput.ParticleIndex = output.ParticleIndex;
#if USE_VERTEX_COLOR
	materialInput.VertexColor = output.VertexColor;
#endif
	materialInput.TBN = output.TBN;
	materialInput.TwoSidedSign = 1;
	materialInput.SvPosition = output.Position;
	materialInput.PreSkinnedPosition = float3(input.Position.xy, 0);
	materialInput.PreSkinnedNormal = tangentToLocal[2].xyz;
	materialInput.InstanceOrigin = output.InstanceOrigin;
	materialInput.InstanceParams = output.InstanceParams;	
	Material material = GetMaterialVS(materialInput);
#endif

	// Apply world position offset per-vertex
#if USE_POSITION_OFFSET
	output.WorldPosition += material.PositionOffset;
	output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix);
#endif

	// Copy interpolants for other shader stages
#if USE_CUSTOM_VERTEX_INTERPOLATORS
	output.CustomVSToPS = material.CustomVSToPS;
#endif

	return output;
}

// Vertex Shader function for Model Rendering
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT,   0, 0,     PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT,      1, 0,     PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(NORMAL,   0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TANGENT,  0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_FLOAT,      1, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(COLOR,    0, R8G8B8A8_UNORM,    2, 0,     PER_VERTEX, 0, USE_VERTEX_COLOR)
VertexOutput VS_Model(ModelInput input, uint particleIndex : SV_InstanceID)
{
	VertexOutput output;

	// Sorted particles mapping
	if (SortedIndicesOffset != 0xFFFFFFFF)
	{
		particleIndex = SortedIndices[SortedIndicesOffset + particleIndex];
	}

	// Read particle data
	float3 particlePosition = GetParticleVec3(particleIndex, PositionOffset);
	float3 particleScale = GetParticleVec3(particleIndex, ScaleOffset);
	float3 particleRotation = GetParticleVec3(particleIndex, RotationOffset);
	int modelFacingMode = ModelFacingModeOffset != -1 ? GetParticleInt(particleIndex, ModelFacingModeOffset) : -1;
	float3 position = mul(float4(particlePosition, 1), WorldMatrix).xyz;

	// Compute final vertex position in the world
	float3x3 eulerMatrix = EulerMatrix(radians(particleRotation));
	float4 transform0 = float4(eulerMatrix[0], particlePosition.x);
	float4 transform1 = float4(eulerMatrix[1], particlePosition.y);
	float4 transform2 = float4(eulerMatrix[2], particlePosition.z);
	float4x4 scaleMatrix = float4x4(float4(particleScale.x, 0.0f, 0.0f, 0.0f), float4(0.0f, particleScale.y, 0.0f, 0.0f), float4(0.0f, 0.0f, particleScale.z, 0.0f), float4(0.0f, 0.0f, 0.0f, 1.0f));
	float4x4 world = float4x4(transform0, transform1, transform2, float4(0.0f, 0.0f, 0.0f, 1.0f));
	if (modelFacingMode == 0)
	{
		// Face Camera Position
		float3 direction = normalize(ViewPos - position);
		float4 alignmentQuat = FindQuatBetween(float3(1.0f, 0.0f, 0.0f), direction);
		float4x4 alignmentMat = QuaternionToMatrix(alignmentQuat);
		world = mul(world, mul(alignmentMat, scaleMatrix));
	}
	else if (modelFacingMode == 1)
	{
		// Face Camera Plane
		float3 direction = -ViewDir;
		float4 alignmentQuat = FindQuatBetween(float3(1.0f, 0.0f, 0.0f), direction);
		float4x4 alignmentMat = QuaternionToMatrix(alignmentQuat);
		world =  mul(world, mul(alignmentMat, scaleMatrix));
	}
	else if (modelFacingMode == 2)
	{
		// Along Velocity
		float3 direction = GetParticleVec3(particleIndex, VelocityOffset);
		float4 alignmentQuat = FindQuatBetween(float3(1.0f, 0.0f, 0.0f), normalize(direction));
		float4x4 alignmentMat = QuaternionToMatrix(alignmentQuat);
		world =  mul(world, mul(alignmentMat, scaleMatrix));
	}
	else
	{
		// Default
		world =  mul(world, scaleMatrix);
	}
	world = transpose(world);
	world =  mul(world, WorldMatrix);

	// Calculate the vertex position in world space
	output.WorldPosition = mul(float4(input.Position, 1), world).xyz;

	// Compute clip space position
	output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix);

	// Pass vertex attributes
	output.TexCoord = input.TexCoord;
	output.ParticleIndex = particleIndex;
#if USE_VERTEX_COLOR
	output.VertexColor = input.Color;
#endif
	output.InstanceOrigin = WorldMatrix[3].xyz;
	output.InstanceParams = PerInstanceRandom;

	// Calculate tanget space to world space transformation matrix for unit vectors
	half3x3 tangentToLocal = CalcTangentToLocal(input);
	half3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal);
	output.TBN = tangentToWorld;

	// Get material input params if need to evaluate any material property
#if USE_POSITION_OFFSET || USE_CUSTOM_VERTEX_INTERPOLATORS
	MaterialInput materialInput = (MaterialInput)0;
	materialInput.WorldPosition = output.WorldPosition;
	materialInput.TexCoord = output.TexCoord;
	materialInput.ParticleIndex = output.ParticleIndex;
#if USE_VERTEX_COLOR
	materialInput.VertexColor = output.VertexColor;
#endif
	materialInput.TBN = output.TBN;
	materialInput.TwoSidedSign = 1;
	materialInput.SvPosition = output.Position;
	materialInput.PreSkinnedPosition = input.Position.xyz;
	materialInput.PreSkinnedNormal = tangentToLocal[2].xyz;
	materialInput.InstanceOrigin = output.InstanceOrigin;
	materialInput.InstanceParams = output.InstanceParams;
	Material material = GetMaterialVS(materialInput);
#endif

	// Apply world position offset per-vertex
#if USE_POSITION_OFFSET
	output.WorldPosition += material.PositionOffset;
	output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix);
#endif

	// Copy interpolants for other shader stages
#if USE_CUSTOM_VERTEX_INTERPOLATORS
	output.CustomVSToPS = material.CustomVSToPS;
#endif

	return output;
}

// Vertex Shader function for Ribbon Rendering
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(TEXCOORD, 0, R32_UINT,  0, 0,     PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 1, R32_UINT,  0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 2, R32_UINT,  0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 3, R32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
VertexOutput VS_Ribbon(RibbonInput input, uint vertexIndex : SV_VertexID)
{
	VertexOutput output;

	// Get particle data
	uint particleIndex = input.ParticleIndex;
	int vertexSign = (((int)vertexIndex & 0x1) * 2) - 1;
	float3 position = GetParticlePosition(particleIndex);
	float ribbonWidth = RibbonWidthOffset != -1 ? GetParticleFloat(particleIndex, RibbonWidthOffset) : 20.0f;
	float ribbonTwist = RibbonTwistOffset != -1 ? GetParticleFloat(particleIndex, RibbonTwistOffset) : 0.0f;

	// Calculate ribbon direction
	float3 direction;
	if (input.Order == 0)
	{
		direction = GetParticlePosition(input.PrevParticleIndex) - position;
	}
	else
	{
		direction = position - GetParticlePosition(input.PrevParticleIndex);
	}

	// Calculate particle orientation (tangent vectors)
	float3 cameraDirection = SafeNormalize(ViewPos - position);
	float3 tangentUp = SafeNormalize(direction);
	float3 facing = RibbonFacingVectorOffset != -1 ? GetParticleVec3(particleIndex, RibbonFacingVectorOffset) : cameraDirection;
	float twistSine, twistCosine;
	sincos(radians(ribbonTwist), twistSine, twistCosine);
	facing = facing * twistCosine + cross(facing, tangentUp) * twistSine + tangentUp * dot(tangentUp, facing) * (1 - twistCosine);
	float3 tangentRight = SafeNormalize(cross(tangentUp, facing));
	if (!any(tangentRight))
	{
		tangentRight = SafeNormalize(cross(tangentUp, float3(0.0f, 0.0f, 1.0f)));
	}

	// Calculate texture coordinates
	if (RibbonUVTilingDistance != 0.0f)
	{
		output.TexCoord.x = input.Distance / RibbonUVTilingDistance;
	}
	else
	{
		output.TexCoord.x = (float)input.Order / (float)RibbonSegmentCount;
	}
	output.TexCoord.y = (vertexIndex + 1) & 0x1;
	output.TexCoord = output.TexCoord * RibbonUVScale + RibbonUVOffset;

	// Compute world space vertex position
	output.WorldPosition = position + tangentRight * vertexSign * (ribbonWidth.xxx * 0.5f);

	// Compute clip space position
	output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix);

	// Pass vertex attributes
	output.ParticleIndex = particleIndex;
#if USE_VERTEX_COLOR
	output.VertexColor = 1;
#endif
	output.InstanceOrigin = WorldMatrix[3].xyz;
	output.InstanceParams = PerInstanceRandom;

	// Calculate tanget space to world space transformation matrix for unit vectors
	half3x3 tangentToLocal = float3x3(tangentRight, tangentUp, cross(tangentRight, tangentUp));
	half3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal);
	output.TBN = tangentToWorld;

	// Get material input params if need to evaluate any material property
#if USE_POSITION_OFFSET || USE_CUSTOM_VERTEX_INTERPOLATORS
	MaterialInput materialInput = (MaterialInput)0;
	materialInput.WorldPosition = output.WorldPosition;
	materialInput.TexCoord = output.TexCoord;
	materialInput.ParticleIndex = output.ParticleIndex;
#if USE_VERTEX_COLOR
	materialInput.VertexColor = output.VertexColor;
#endif
	materialInput.TBN = output.TBN;
	materialInput.TwoSidedSign = 1;
	materialInput.SvPosition = output.Position;
	materialInput.PreSkinnedPosition = Position;
	materialInput.PreSkinnedNormal = tangentToLocal[2].xyz;
	materialInput.InstanceOrigin = output.InstanceOrigin;
	materialInput.InstanceParams = output.InstanceParams;	
	Material material = GetMaterialVS(materialInput);
#endif

	// Apply world position offset per-vertex
#if USE_POSITION_OFFSET
	output.WorldPosition += material.PositionOffset;
	output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix);
#endif

	// Copy interpolants for other shader stages
#if USE_CUSTOM_VERTEX_INTERPOLATORS
	output.CustomVSToPS = material.CustomVSToPS;
#endif

	return output;
}

// Pixel Shader function for Depth Pass
META_PS(true, FEATURE_LEVEL_ES2)
void PS_Depth(PixelInput input)
{
	// Get material parameters
	MaterialInput materialInput = GetMaterialInput(input);
	Material material = GetMaterialPS(materialInput);

	// Perform per pixel clipping
#if MATERIAL_MASKED
	clip(material.Mask - MATERIAL_MASK_THRESHOLD);
#endif
#if MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT
	clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD);
#endif
}

#if _PS_QuadOverdraw

#include "./Flax/Editor/QuadOverdraw.hlsl"

// Pixel Shader function for Quad Overdraw Pass (editor-only)
[earlydepthstencil]
META_PS(USE_EDITOR, FEATURE_LEVEL_SM5)
void PS_QuadOverdraw(float4 svPos : SV_Position, uint primId : SV_PrimitiveID)
{
	DoQuadOverdraw(svPos, primId);
}

#endif

@9
