#if SHADOWS_ON_TRANSPARENTS == 0
	#undef SHADOWS_TYPE
	#define SHADOWS_TYPE 0
#endif

in_out vec2 texcoord;
in_out vec2 lmcoord;
in_out vec4 glcolor;
in_out vec3 viewPos;
in_out vec3 playerPos;
#if PBR_TYPE == 0
	flat in_out vec3 normal;
#endif
flat in_out uint materialId;
#if PBR_TYPE == 0
	flat in_out float reflectiveness;
	flat in_out float specularness;
#endif

flat in_out vec2 midTexCoord;
flat in_out vec2 midCoordOffset;

#if WAVING_WATER_SURFACE_ENABLED == 1 || FANCY_NETHER_PORTAL_ENABLED == 1 || PBR_TYPE == 1
	in_out mat3 tbn;
#endif
#if BORDER_FOG_ENABLED == 1
	in_out float fogAmount;
#endif



#ifdef FSH

#include "/lib/lighting/fsh_lighting.glsl"
#include "/utils/depth.glsl"
#include "/utils/projections.glsl"

#if WAVING_WATER_SURFACE_ENABLED == 1
	#include "/lib/simplex_noise.glsl"
#endif

void main() {
	
	
	// fade distant terrain
	#ifdef DISTANT_HORIZONS
		float dither = bayer64(gl_FragCoord.xy);
		#if TEMPORAL_FILTER_ENABLED > 0
			dither = fract(dither + 1.61803398875 * mod(float(frameCounter), 3600.0));
		#endif
		float lengthCylinder = max(length(playerPos.xz), abs(playerPos.y));
		if (lengthCylinder >= far - 8.0 - 8.0 * dither) discard;
	#elif defined VOXY
		
	#elif CYLINDRICAL_CLIPPING == 1
		float fogDistance = max(length(playerPos.xz), abs(playerPos.y));
		fogDistance *= invFar;
		if (fogDistance >= BORDER_FOG_END) {discard; return;}
	#endif
	
	
	// get pbr data
	#if PBR_TYPE == 0
		float reflectiveness = reflectiveness;
	#elif PBR_TYPE == 1
		vec2 pbrData = texture2D(specular, texcoord).rg;
		float reflectiveness = pbrData.g;
		reflectiveness = mix(reflectiveness, mix(WATER_REFLECTION_AMOUNT_UNDERGROUND, WATER_REFLECTION_AMOUNT_SURFACE, lmcoord.y), float(materialId == BLOCK_ID_WATER));
		float specularness = sqrt(pbrData.r);
		vec3 normal = texture2D(normals, texcoord).rgb;
		normal.xy -= 0.5;
		normal.xy *= PBR_NORMALS_AMOUNT * 0.75;
		normal.xy += 0.5;
		normal = normalize(normal * 2.0 - 1.0);
		normal = tbn * normal;
	#endif
	float reflectivenessMult = mix(BLOCK_REFLECTION_AMOUNT_SURFACE, BLOCK_REFLECTION_AMOUNT_UNDERGROUND, lmcoord.y);
	reflectiveness *= 1.0 - float(materialId != BLOCK_ID_WATER) * (1.0 - reflectivenessMult);
	
	
	// get texture color
	vec4 rawColor = texture2D(MAIN_TEXTURE, texcoord);
	if (rawColor.a < 0.01) discard;
	vec4 color = rawColor;
	color.rgb = color.rgb - (4.0 / 27.0) * color.rgb * color.rgb * color.rgb;
	color *= glcolor;
	
	float m = getLum(color.rgb);
	m = m * m * (3.0 - 2.0 * m);
	color.rgb *= 1.0 - TEXTURE_CONTRAST * 0.125 + m * TEXTURE_CONTRAST * 0.25;
	color.rgb = color.rgb * (1.0 + TEXTURE_CONTRAST_2 * 0.025) - TEXTURE_CONTRAST_2 * 0.025;
	
	
	// misc
	
	#if PBR_TYPE == 0
		reflectiveness *= 1.0 - 0.5 * getSaturation(rawColor.rgb);
	#endif
	
	#if WAVING_WATER_SURFACE_ENABLED == 1 && PBR_TYPE == 0
		vec3 normal = normal;
	#endif
	
	
	// block-specific effects
	
	
	// water
	if (materialId == BLOCK_ID_WATER) {
		
		color.rgb = mix(glcolor.rgb, color.rgb, WATER_TEXTURE_INFLUENCE);
		color.rgb = mix(vec3(getLum(color.rgb)), color.rgb, WATER_BIOME_INFLUENCE);
		
		vec3 viewDir = normalize(viewPos);
		float fresnel = -dot(normal, viewDir);
		
		
		#if WAVING_WATER_SURFACE_ENABLED == 1
			vec2 noisePos = (playerPos.xz + cameraPosition.xz) / WAVING_WATER_SCALE * 0.25;
			float wavingSurfaceAmount = mix(WAVING_WATER_SURFACE_AMOUNT_UNDERGROUND, WAVING_WATER_SURFACE_AMOUNT_SURFACE, lmcoord.y) * fresnel * 0.125;
			vec2 rawNormalNoise = vec2(0.0);
			if (wavingSurfaceAmount > 0.00001) {
				float fresnelMult = mix(WAVING_WATER_FRESNEL_UNDERGROUND * 0.55, WAVING_WATER_FRESNEL_SURFACE * 0.55, lmcoord.y);
				float frameTimeCounter = frameTimeCounter * WAVING_WATER_SPEED;
				noisePos += (texture2D(noisetex, noisePos * 0.03125 + frameTimeCounter * vec2( 0.01,  0.01)).br * 2.0 - 1.0) * 0.4 * 0.18;
				noisePos += (texture2D(noisetex, noisePos * 0.0625  + frameTimeCounter * vec2( 0.01, -0.01)).br * 2.0 - 1.0) * 0.25 * 0.18;
				noisePos += (texture2D(noisetex, noisePos * 0.0625  + frameTimeCounter * vec2(-0.01,  0.01)).br * 2.0 - 1.0) * 0.25 * 0.18;
				noisePos += (texture2D(noisetex, noisePos * 0.03125 + frameTimeCounter * vec2( 0.01,  0.01)).br * 2.0 - 1.0) * 0.4 * 0.18;
				noisePos += (texture2D(noisetex, noisePos * 0.0625  + frameTimeCounter * vec2( 0.01, -0.01)).br * 2.0 - 1.0) * 0.25 * 0.18;
				noisePos += (texture2D(noisetex, noisePos * 0.0625  + frameTimeCounter * vec2(-0.01,  0.01)).br * 2.0 - 1.0) * 0.25 * 0.18;
				normal = normalize(transpose(tbn) * normal);
				normal.xy += (texture2D(noisetex, noisePos * 0.03125 + frameTimeCounter * vec2( 0.01,  0.01)).br * 2.0 - 1.0) * 0.4;
				normal.xy += (texture2D(noisetex, noisePos * 0.0625  + frameTimeCounter * vec2( 0.01, -0.01)).br * 2.0 - 1.0) * 0.25;
				normal.xy += (texture2D(noisetex, noisePos * 0.0625  + frameTimeCounter * vec2(-0.01,  0.01)).br * 2.0 - 1.0) * 0.25;
				rawNormalNoise = normal.xy;
				vec3 normalWithoutMult = tbn * normalize(normal);
				normal.xy *= wavingSurfaceAmount;
				normal = tbn * normalize(normal);
				fresnel = -dot(normalWithoutMult, viewDir);
				color.rgb *= 1.0 + (0.5 - fresnel) * fresnelMult;
			}
		#endif
		
		
		float opaqueDepth = texelFetch(DEPTH_BUFFER_WO_TRANS, texelcoord, 0).r;
		vec3 opaqueViewPos = screenToView(vec3(texelcoord * pixelSize, opaqueDepth));
		float blockDepth = length(viewPos);
		float opaqueBlockDepth = length(opaqueViewPos);
		#ifdef DISTANT_HORIZONS
			//float opaqueDepthDh = texelFetch(DH_DEPTH_BUFFER_WO_TRANS, texelcoord, 0).r;
			//if (opaqueBlockDepth > 0) {
			//	vec3 opaqueViewPosDh = screenToViewDh(vec3(texelcoord * pixelSize, opaqueDepthDh));
			//	float opaqueBlockDepthDh = length(opaqueViewPosDh);
			//	opaqueBlockDepth = min(opaqueBlockDepth, opaqueBlockDepthDh);
			//}
		#endif
		#if BORDER_FOG_ENABLED == 1
			// this tries to fix underwater border fog but it breaks more stuff than it fixes
			//vec3 opaquePlayerPos = mat3(gbufferModelViewInverse) * opaqueViewPos;
			//opaqueBlockDepth = mix(opaqueBlockDepth, far, getBorderFogAmount(opaquePlayerPos));
		#endif
		float waterDepth = opaqueBlockDepth - blockDepth;
		float waterDepthPercent = exp(waterDepth / -8.0); // note: 0.0 is deep and 1.0 is shallow
		if (isEyeInWater == 1) {
			color.a = 1.0 - WATER_TRANSPARENCY_DEEP;
		} else {
			color.a = 1.0 - mix(WATER_TRANSPARENCY_DEEP, WATER_TRANSPARENCY_SHALLOW, waterDepthPercent);
		}
		color.a *= 1.0 - fresnel * 0.125;
		
		color.rgb *= mix(WATER_TINT_DEEP, WATER_TINT_SHALLOW, waterDepthPercent);
		
		#if WATER_FOAM_ENABLED == 1
			float foamAmount = percentThrough(waterDepth, WATER_FOAM_SIZE, 0.0);
			reflectiveness *= 1.0 - foamAmount;
			foamAmount *= foamAmount;
			foamAmount *= 1.0 - 0.25 * fresnel;
			foamAmount *= 0.5 + 0.5 * lmcoord.y;
			#if WAVING_WATER_SURFACE_ENABLED == 1
				foamAmount *= 0.5 + 0.65 * length(rawNormalNoise);
			#else
				foamAmount *= 0.7;
			#endif
			if (isEyeInWater > 0) foamAmount *= 0.5;
			color.rgb = mix(color.rgb, vec3(0.75 + 0.25 * dayPercent), foamAmount * WATER_FOAM_AMOUNT * 2.0);
		#endif
		
		// water needs to be more opaque in dark areas
		float alphaLift = max(lmcoord.x, lmcoord.y * dayPercent);
		alphaLift = sqrt(alphaLift);
		alphaLift = (1.0 - alphaLift) * (1.2 - screenBrightness);
		#if WATER_FOAM_ENABLED == 1
			alphaLift += foamAmount * WATER_FOAM_AMOUNT;
		#endif
		color.a = 1.0 - (1.0 - alphaLift) * (1.0 - color.a);
		
	}
	
	
	// nether portal
	#if FANCY_NETHER_PORTAL_ENABLED == 1
		if (materialId == BLOCK_ID_NETHER_PORTAL) {
			vec3 tangentViewDir = normalize(transpose(tbn) * viewPos);
			tangentViewDir.x *= -1.0;
			
			vec2 texcoordInt = texcoord * atlasSize;
			vec2 distToNext = fract(texcoordInt);
			
			distToNext = mix(1.0 - distToNext, distToNext, step(0.0, tangentViewDir.xy)); // invert distToNext depending on direction of tangentViewDir
			distToNext /= abs(tangentViewDir.xy);
			
			tangentViewDir *= min(distToNext.x, distToNext.y);
			float dist = length(tangentViewDir);
			
			ivec2 neighborCoord = ivec2(texcoordInt);
			neighborCoord -= distToNext.x < distToNext.y ? ivec2(sign(tangentViewDir.x), 0) : ivec2(0, sign(tangentViewDir.y)); // I'm not entirely sure why it's -= instead of +=, must be coord space shenanigans
			
			ivec2 minCoord = ivec2((midTexCoord - midCoordOffset) * atlasSize);
			ivec2 maxCoord = ivec2((midTexCoord + midCoordOffset) * atlasSize + 1.0);
			neighborCoord -= minCoord;
			neighborCoord %= maxCoord - minCoord;
			neighborCoord += minCoord;
			
			vec4 neighborPixel = texelFetch(MAIN_TEXTURE, neighborCoord, 0);
			neighborPixel /= 1.0 + dist * 0.1;
			color = max(color, neighborPixel * 0.9);
			
		}
	#endif
	
	
	// main lighting
	float _inSunlightAmount;
	doFshLighting(color.rgb, _inSunlightAmount, lmcoord.x, lmcoord.y, specularness, 0.0, viewPos, normal, gl_FragCoord.z);
	
	
	// fog
	#if BORDER_FOG_ENABLED == 1
		color.a *= 1.0 - fogAmount;
	#endif
	
	
	/* DRAWBUFFERS:03 */
	#if DO_COLOR_CODED_GBUFFERS == 1
		color = vec4(0.0, 0.0, 1.0, 1.0);
	#endif
	color.rgb *= 0.5;
	gl_FragData[0] = color;
	gl_FragData[1] = vec4(
		pack_2x8(lmcoord),
		pack_7_7_1_1(reflectiveness, specularness, 0.0, 0.0),
		encodeNormal(normal)
	);
	
}

#endif



#ifdef VSH

#include "/utils/projections.glsl"
#include "/lib/lighting/vsh_lighting.glsl"

#if TAA_ENABLED == 1
	#include "/lib/taa_jitter.glsl"
#endif
#if BORDER_FOG_ENABLED == 1
	#include "/utils/borderFogAmount.glsl"
#endif

void main() {
	texcoord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
	lmcoord  = (gl_TextureMatrix[1] * gl_MultiTexCoord1).xy;
	adjustLmcoord(lmcoord);
	glcolor = gl_Color;
	viewPos = transform(gl_ModelViewMatrix, gl_Vertex.xyz);
	playerPos = transform(gbufferModelViewInverse, viewPos);
	#if PBR_TYPE != 0
		vec3 normal;
	#endif
	normal = gl_NormalMatrix * gl_Normal;
	
	uint encodedData = uint(mc_Entity.x + 0.5);
	encodedData *= uint((encodedData & (1u << 14u)) > 0u && encodedData != 65535u);
	materialId = encodedData;
	materialId &= (1u << 10u) - 1u;
	
	#if PBR_TYPE == 0
		#define GET_REFLECTIVENESS
		#define GET_SPECULARNESS
	#endif
	#define DO_BRIGHTNESS_TWEAKS
	#include "/generated/blockDatas.glsl"
	
	midTexCoord = mat2(gl_TextureMatrix[0]) * mc_midTexCoord;
	midCoordOffset = abs(texcoord - midTexCoord);
	
	
	#if WAVING_WATER_SURFACE_ENABLED == 1 || FANCY_NETHER_PORTAL_ENABLED == 1 || PBR_TYPE == 1
		vec3 tangent = normalize(gl_NormalMatrix * at_tangent.xyz);
		vec3 bitangent = normalize(cross(normal, tangent) * at_tangent.w);
		tbn = mat3(tangent, bitangent, normal);
	#endif
	
	
	#if PHYSICALLY_WAVING_WATER_ENABLED == 1
		if (materialId == BLOCK_ID_WATER) {
			float wavingAmount = mix(PHYSICALLY_WAVING_WATER_AMOUNT_UNDERGROUND, PHYSICALLY_WAVING_WATER_AMOUNT_SURFACE, lmcoord.y);
			#ifdef DISTANT_HORIZONS
				float lengthCylinder = max(length(playerPos.xz), abs(playerPos.y));
				wavingAmount *= smoothstep(far * 0.95 - 10, far * 0.9 - 10, lengthCylinder);
			#endif
			playerPos += cameraPosition;
			playerPos.y += sin(playerPos.x * 0.6 + playerPos.z * 1.4 + frameTimeCounter * 3.0) * 0.015 * wavingAmount;
			playerPos.y += sin(playerPos.x * 0.9 + playerPos.z * 0.6 + frameTimeCounter * 2.5) * 0.01 * wavingAmount;
			playerPos -= cameraPosition;
			playerPos.y += 0.1 / (1.0 + length(playerPos.xz)) * sign(isEyeInWater - 0.5);
			#if defined VOXY && MC_VERSION <= 12001
				playerPos.y -= length(playerPos.xz) * 0.001;
			#endif
		}
	#endif
	
	
	gl_Position = playerToNdc(playerPos);
	
	
	#if TAA_ENABLED == 1
		doTaaJitter(gl_Position.xy);
	#endif
	
	
	#if BORDER_FOG_ENABLED == 1
		float _fogDistance;
		fogAmount = getBorderFogAmount(playerPos, _fogDistance);
	#endif
	
	
	doVshLighting(lmcoord, glcolor.rgb, viewPos, normal, gl_Normal);
	
}

#endif
