#ifndef VERTEX_LIT_FORWARD_PASS_URP_INCLUDED
#define VERTEX_LIT_FORWARD_PASS_URP_INCLUDED

#include "Packages/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Sprite-Common-URP.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.esotericsoftware.spine.urp-shaders/Shaders/Include/SpineCoreShaders/SpriteLighting.cginc"
#include "Packages/com.esotericsoftware.spine.urp-shaders/Shaders/Include/SpineCoreShaders/Spine-Common.cginc"
#include "Packages/com.esotericsoftware.spine.urp-shaders/Shaders/Include/Spine-Common-URP.hlsl"

#if defined(_ALPHAPREMULTIPLY_ON)
	#undef _STRAIGHT_ALPHA_INPUT
#elif !defined(_STRAIGHT_ALPHA_INPUT)
	#define _STRAIGHT_ALPHA_INPUT
#endif
#include "Packages/com.esotericsoftware.spine.urp-shaders/Shaders/Include/SpineCoreShaders/Spine-Skeleton-Tint-Common.cginc"

#if !defined(DYNAMICLIGHTMAP_ON) && !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) && defined(__PROBEVOLUME_HLSL__)
#define USE_ADAPTIVE_PROBE_VOLUMES
#endif

#if defined(_RIM_LIGHTING) || defined(_ADDITIONAL_LIGHTS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS) || defined(USE_LIGHT_COOKIES) || defined(USE_ADAPTIVE_PROBE_VOLUMES)
	#define NEEDS_POSITION_WS
#endif

////////////////////////////////////////
// Vertex output struct
//
struct VertexOutputLWRP
{
	float4 pos : SV_POSITION;
	fixed4 vertexColor : COLOR;
	float3 texcoord : TEXCOORD0;

	half4 fogFactorAndVertexLight : TEXCOORD1;
	half3 viewDirectionWS : TEXCOORD2;

	DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 3);

#if defined(_NORMALMAP)
	half4 normalWorld : TEXCOORD4;
	half4 tangentWorld : TEXCOORD5;
	half4 binormalWorld : TEXCOORD6;
#else
	half3 normalWorld : TEXCOORD4;
#endif
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
	float4 shadowCoord : TEXCOORD7;
#endif
#if defined(NEEDS_POSITION_WS)
	float4 positionWS : TEXCOORD8;
#endif
#if defined(_TINT_BLACK_ON)
	float3 darkColor : TEXCOORD9;
#endif
#if defined(USE_ADAPTIVE_PROBE_VOLUMES)
	float3 positionCS : TEXCOORD10;
#endif

	UNITY_VERTEX_OUTPUT_STEREO
};

///////////////////////////////////////////////////////////////////////////////
//                  Vertex and Fragment functions                            //
///////////////////////////////////////////////////////////////////////////////
#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
half4 CalculateShadowMaskBackwardsCompatible(InputData inputData)
{
	// To ensure backward compatibility we have to avoid using shadowMask input, as it is not present in older shaders
#if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)
	half4 shadowMask = inputData.shadowMask;
#elif !defined (LIGHTMAP_ON)
	half4 shadowMask = unity_ProbesOcclusion;
#else
	half4 shadowMask = half4(1, 1, 1, 1);
#endif

	return shadowMask;
}
#endif

half3 LightweightLightVertexSimplified(float3 positionWS, half3 normalWS) {
#ifdef _MAIN_LIGHT_VERTEX
	Light mainLight = GetMainLight();
	half3 attenuatedLightColor = mainLight.color * (mainLight.distanceAttenuation * mainLight.shadowAttenuation);
	half3 diffuseLightColor = LightingLambert(attenuatedLightColor, mainLight.direction, normalWS);
#else
	half3 diffuseLightColor = half3(0, 0, 0);
#endif

#ifdef _ADDITIONAL_LIGHTS_VERTEX
	int pixelLightCount = GetAdditionalLightsCount();
	for (int i = 0; i < pixelLightCount; ++i)
	{
		Light light = GetAdditionalLight(i, positionWS);
		half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
		diffuseLightColor += LightingLambert(attenuatedLightColor, light.direction, normalWS);
	}
#endif // _ADDITIONAL_LIGHTS_VERTEX
	return diffuseLightColor;
}

#ifdef _DIFFUSE_RAMP
half3 LightingLambertRamped(half3 lightColor, float attenuation, half3 lightDir, half3 normal)
{
	half angleDot = max(0, dot(lightDir, normal));
	return calculateRampedDiffuse(lightColor, attenuation, angleDot);
}
#endif

#if defined(SPECULAR)

half3 ProcessLightPBRSimplified(InputData inputData, BRDFData brdfData, half4 shadowMask, uint meshRenderingLayers, int lightIndex)
{
#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
	Light light = GetAdditionalLight(lightIndex, inputData.positionWS, shadowMask);
#else
	Light light = GetAdditionalLight(lightIndex, inputData.positionWS);
#endif
#ifdef USE_LIGHT_LAYERS
	if (!IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
		return half3(0, 0, 0);
#endif
	return LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS);
}

half4 LightweightFragmentPBRSimplified(InputData inputData, half4 texAlbedoAlpha, half metallic, half3 specular,
	half smoothness, half3 emission, half4 vertexColor)
{
#if !defined(_TINT_BLACK_ON)
	half4 albedo = texAlbedoAlpha * vertexColor;
#else
	half4 albedo = texAlbedoAlpha;
#endif

	BRDFData brdfData;
	half ignoredAlpha = 1; // ignore alpha, otherwise
	InitializeBRDFData(albedo.rgb, metallic, specular, smoothness, ignoredAlpha, brdfData);
	brdfData.specular *= albedo.a;

#ifndef _MAIN_LIGHT_VERTEX
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
	Light mainLight = GetMainLight(inputData.shadowCoord);
#else
	Light mainLight = GetMainLight();
#endif
#if defined(USE_LIGHT_COOKIES)
	half3 cookieColor = SampleMainLightCookie(inputData.positionWS);
	mainLight.color *= cookieColor;
#endif

	half3 finalColor = inputData.bakedGI * albedo.rgb;
	finalColor += LightingPhysicallyBased(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS);
#else // _MAIN_LIGHT_VERTEX
	half3 finalColor = inputData.bakedGI * albedo.rgb;
#endif // _MAIN_LIGHT_VERTEX

#ifdef _ADDITIONAL_LIGHTS
	uint meshRenderingLayers = GetMeshRenderingLayerBackwardsCompatible();

#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
	half4 shadowMask = CalculateShadowMaskBackwardsCompatible(inputData);
#else
	half4 shadowMask = half4(1, 1, 1, 1);
#endif

#if USE_FORWARD_PLUS
	for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
	{
		FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
		finalColor += ProcessLightPBRSimplified(inputData, brdfData, shadowMask, meshRenderingLayers, lightIndex);
	}
#endif
	uint pixelLightCount = GetAdditionalLightsCount();
	LIGHT_LOOP_BEGIN_SPINE(pixelLightCount)
		finalColor += ProcessLightPBRSimplified(inputData, brdfData, shadowMask, meshRenderingLayers, lightIndex);
	LIGHT_LOOP_END_SPINE

#endif // _ADDITIONAL_LIGHTS

#ifdef _ADDITIONAL_LIGHTS_VERTEX
	finalColor += inputData.vertexLighting * brdfData.diffuse;
#endif
	finalColor += emission;
	return prepareLitPixelForOutput(half4(finalColor, albedo.a), texAlbedoAlpha.a, vertexColor.a);
}

#else // !SPECULAR

half3 ProcessLightLambert(InputData inputData, half4 shadowMask, uint meshRenderingLayers, int lightIndex)
{
#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
	Light light = GetAdditionalLight(lightIndex, inputData.positionWS, shadowMask);
#else
	Light light = GetAdditionalLight(lightIndex, inputData.positionWS);
#endif

#ifdef USE_LIGHT_LAYERS
	if (!IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
		return half3(0, 0, 0);
#endif

	half3 attenuation = (light.distanceAttenuation * light.shadowAttenuation);
	half3 attenuatedLightColor = light.color * attenuation;
#ifndef _DIFFUSE_RAMP
	return LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS);
#else
	return LightingLambertRamped(light.color, attenuation, light.direction, inputData.normalWS);
#endif
}

half4 LightweightFragmentBlinnPhongSimplified(InputData inputData, half4 texDiffuseAlpha, half3 emission, half4 vertexColor)
{
#if !defined(_TINT_BLACK_ON)
	half4 diffuse = texDiffuseAlpha * vertexColor;
#else
	half4 diffuse = texDiffuseAlpha;
#endif

#ifndef _MAIN_LIGHT_VERTEX
#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
	Light mainLight = GetMainLight(inputData.shadowCoord);
#else
	Light mainLight = GetMainLight();
#endif
#if defined(USE_LIGHT_COOKIES)
	half3 cookieColor = SampleMainLightCookie(inputData.positionWS);
	mainLight.color *= cookieColor;
#endif

	half3 diffuseLighting = inputData.bakedGI;

	half3 attenuation = mainLight.distanceAttenuation* mainLight.shadowAttenuation;
	half3 attenuatedLightColor = mainLight.color * attenuation;
#ifndef _DIFFUSE_RAMP
	diffuseLighting += LightingLambert(attenuatedLightColor, mainLight.direction, inputData.normalWS);
#else
	diffuseLighting += LightingLambertRamped(mainLight.color, attenuation, mainLight.direction, inputData.normalWS);
#endif
#else // _MAIN_LIGHT_VERTEX
	half3 diffuseLighting = inputData.bakedGI;
#endif // _MAIN_LIGHT_VERTEX

#ifdef _ADDITIONAL_LIGHTS
	uint meshRenderingLayers = GetMeshRenderingLayerBackwardsCompatible();

#if defined(_ADDITIONAL_LIGHT_SHADOWS) && !defined(_RECEIVE_SHADOWS_OFF)
	half4 shadowMask = CalculateShadowMaskBackwardsCompatible(inputData);
#else
	half4 shadowMask = half4(1, 1, 1, 1);
#endif
#if USE_FORWARD_PLUS
	for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
	{
		FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
		diffuseLighting += ProcessLightLambert(inputData, shadowMask, meshRenderingLayers, lightIndex);
	}
#endif
	uint pixelLightCount = GetAdditionalLightsCount();
	LIGHT_LOOP_BEGIN_SPINE(pixelLightCount)
		diffuseLighting += ProcessLightLambert(inputData, shadowMask, meshRenderingLayers, lightIndex);
	LIGHT_LOOP_END_SPINE

#endif
#ifdef _ADDITIONAL_LIGHTS_VERTEX
	diffuseLighting += inputData.vertexLighting;
#endif
	diffuseLighting += emission;
	//half3 finalColor = diffuseLighting * diffuse + emission;
	half3 finalColor = diffuseLighting * diffuse.rgb;
	return prepareLitPixelForOutput(half4(finalColor, diffuse.a), texDiffuseAlpha.a, vertexColor.a);
}
#endif // SPECULAR

VertexOutputLWRP ForwardPassVertexSprite(VertexInput input)
{
	VertexOutputLWRP output = (VertexOutputLWRP)0;

	UNITY_SETUP_INSTANCE_ID(input);
	UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

	output.pos = calculateLocalPos(input.vertex);
	output.vertexColor = calculateVertexColor(input.color);
#if defined(_TINT_BLACK_ON)
	output.darkColor = GammaToTargetSpace(
		half3(input.tintBlackRG.r, input.tintBlackRG.g, input.tintBlackB.r)) + (_Black.rgb * input.color.a);
#endif
	output.texcoord = float3(calculateTextureCoord(input.texcoord), 0);

	float3 positionWS = TransformObjectToWorld(input.vertex.xyz);

	float backFaceSign = 1;
#if defined(FIXED_NORMALS_BACKFACE_RENDERING)
	backFaceSign = calculateBackfacingSign(positionWS.xyz);
#endif
	output.viewDirectionWS = GetCameraPositionWS() - positionWS;
#if defined(NEEDS_POSITION_WS)
	output.positionWS = float4(positionWS, 1);
#endif

	half3 normalWS = calculateSpriteWorldNormal(input, -backFaceSign);
	output.normalWorld.xyz = normalWS;
#if defined(_NORMALMAP)
	output.tangentWorld.xyz = calculateWorldTangent(input.tangent);
	output.binormalWorld.xyz = calculateSpriteWorldBinormal(input, output.normalWorld.xyz, output.tangentWorld.xyz, backFaceSign);
#endif

	output.fogFactorAndVertexLight.yzw = LightweightLightVertexSimplified(positionWS, normalWS);

#if (defined(_MAIN_LIGHT_SHADOWS) || defined(MAIN_LIGHT_CALCULATE_SHADOWS)) && !defined(_RECEIVE_SHADOWS_OFF)
	VertexPositionInputs vertexInput;
	vertexInput.positionWS = positionWS;
	vertexInput.positionCS = output.pos;
	output.shadowCoord = GetShadowCoord(vertexInput);
#endif

#if defined(_FOG)
	half fogFactor = ComputeFogFactor(output.pos.z);
	output.fogFactorAndVertexLight.x = fogFactor;
#endif

#if IS_URP_15_OR_NEWER
	#ifdef OUTPUT_SH4
		#if IS_URP_17_OR_NEWER
			float4 ignoredProbeOcclusion;
			OUTPUT_SH4(positionWS, normalWS.xyz, GetWorldSpaceNormalizeViewDir(positionWS), output.vertexSH, ignoredProbeOcclusion);
		#else // 15 or newer
			OUTPUT_SH4(positionWS, normalWS.xyz, GetWorldSpaceNormalizeViewDir(positionWS), output.vertexSH);
		#endif
	#else
		OUTPUT_SH(positionWS, normalWS.xyz, GetWorldSpaceNormalizeViewDir(positionWS), output.vertexSH);
	#endif
#else
	OUTPUT_SH(normalWS.xyz, output.vertexSH);
#endif
#if defined(USE_ADAPTIVE_PROBE_VOLUMES)
	output.positionCS = output.pos;
#endif
	return output;
}

half4 ForwardPassFragmentSprite(VertexOutputLWRP input
#ifdef USE_WRITE_RENDERING_LAYERS
	, out float4 outRenderingLayers : SV_Target1
#endif
) : SV_Target0
{
	UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

	fixed4 texureColor = calculateTexturePixel(input.texcoord.xy);

	RETURN_UNLIT_IF_ADDITIVE_SLOT_TINT(texureColor, input.vertexColor, input.darkColor, _Color.a, _Black.a) // shall be called before ALPHA_CLIP

	ALPHA_CLIP(texureColor, input.vertexColor)

#if defined(_TINT_BLACK_ON)
	texureColor = fragTintedColor(texureColor, input.darkColor, input.vertexColor, _Color.a, _Black.a);
#endif

	// fill out InputData struct
	InputData inputData;
#if !defined(_RECEIVE_SHADOWS_OFF)
	#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
		inputData.shadowCoord = input.shadowCoord;
	#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
		inputData.shadowCoord = TransformWorldToShadowCoord(input.positionWS.xyz);
	#elif defined(_MAIN_LIGHT_SHADOWS)
		inputData.shadowCoord = input.shadowCoord;
	#else
		inputData.shadowCoord = float4(0, 0, 0, 0);
	#endif
#endif

	inputData.viewDirectionWS = input.viewDirectionWS;
	inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;

#if defined(PER_PIXEL_LIGHTING) && defined(_NORMALMAP)
	half3 normalWS = calculateNormalFromBumpMap(input.texcoord.xy, input.tangentWorld.xyz, input.binormalWorld.xyz, input.normalWorld.xyz);
#else
	half3 normalWS = input.normalWorld.xyz;
#endif

	inputData.normalWS = normalWS;
#if defined(USE_ADAPTIVE_PROBE_VOLUMES)
	half4 shadowMask = 1.0;
	float4 ignoredProbeOcclusion;
	inputData.bakedGI = SAMPLE_GI(input.vertexSH,
		GetAbsolutePositionWS(input.positionWS),
		inputData.normalWS.xyz,
		GetWorldSpaceNormalizeViewDir(input.positionWS),
		input.positionCS.xy,
		ignoredProbeOcclusion,
		shadowMask);
#else // USE_ADAPTIVE_PROBE_VOLUMES
	inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
#endif

#if defined(_RIM_LIGHTING) || defined(_ADDITIONAL_LIGHTS) || defined(USE_LIGHT_COOKIES)
	inputData.positionWS = input.positionWS.rgb;
#endif
#if defined(_ADDITIONAL_LIGHTS) && USE_FORWARD_PLUS
	inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.pos);
	// note: don't assign normalizedScreenSpaceUV otherwise since old URP versions are missing this member variable
#endif

#if defined(SPECULAR)
	half2 metallicGloss = getMetallicGloss(input.texcoord.xy);
	half metallic = metallicGloss.x;
	half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.

	half3 specular = half3(0, 0, 0);
	half4 emission = half4(0, 0, 0, 1);
	APPLY_EMISSION_SPECULAR(emission, input.texcoord.xy)
	half4 pixel = LightweightFragmentPBRSimplified(inputData, texureColor, metallic, specular, smoothness, emission.rgb, input.vertexColor);
#else
	half3 emission = half3(0, 0, 0);
	APPLY_EMISSION(emission, input.texcoord.xy)
	half4 pixel = LightweightFragmentBlinnPhongSimplified(inputData, texureColor, emission, input.vertexColor);
#endif

#if defined(_RIM_LIGHTING)
	pixel.rgb = applyRimLighting(input.positionWS.xyz, normalWS, pixel);
#endif

	COLORISE(pixel)
	APPLY_FOG_LWRP(pixel, input.fogFactorAndVertexLight.x)

#ifdef USE_WRITE_RENDERING_LAYERS
	uint renderingLayers = GetMeshRenderingLayerBackwardsCompatible();
	outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0);
#endif

	return pixel;
}

#endif
