#version 120

//#define CROSS_PROCESS //Opposite of desaturation, makes everything more vibrant and saturated.
#define DYNAMIC_LIGHT_FLICKER 8 //How much certain dynamic lights (like torches) will flicker [0 1 2 3 4 5 6 8 12 16 24 32 48 64]
#define DYNAMIC_LIGHTS //Holding blocks that emit light will light up their surroundings
#define HEAT_REFRACTION 1.00 //How much the screen jiggles around in the nether, or when in lava [0.00 0.25 0.50 0.75 1.00 1.25 1.50 1.75 2.00 2.25 2.50 2.75 3.00 3.25 3.50 3.75 4.00 4.25 4.50 4.75 5.00]
#define GRASS_AO //Adds ambient occlusion to tallgrass/flowers/etc... Works best with "Remove Y Offset" enabled.
#define JUMPING_DRAGON_EGGS //Makes dragon eggs jump and wobble, as if they were about to hatch
#define LAVA_PATCHES //Randomizes lava brightness, similar to grass patches
//#define REMOVE_XZ_OFFSET //Removes random X/Z offset from tallgrass/flowers/etc...
//#define REMOVE_Y_OFFSET //Removes random Y offset from tallgrass/flowers/etc...

#define LAVA_WAVE_STRENGTH 100 //Adds waves to the nether lava oceans [0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100]
#define NETHER_LAVA_LEVEL 32.0 //Y level at which lava waves happen. Also controls some soul fire integration logic. [0.0 8.0 16.0 24.0 32.0 40.0 48.0 56.0 64.0 72.0 80.0 88.0 96.0 104.0 112.0 120.0 128.0 136.0 144.0 152.0 160.0 168.0 176.0 184.0 192.0 200.0 208.0 216.0 224.0 232.0 240.0 248.0 256.0]
#define SOUL_FIRE_INTEGRATION //When enabled, block light will be tinted blue in locations that soul fire could potentially spawn at
#define SOUL_LAVA //When enabled, lava and magma blocks will be blue tinted in soul sand valleys

attribute vec2 mc_midTexCoord;
attribute vec3 mc_Entity;

uniform float frameTimeCounter;
uniform float inSoulSandValley;
uniform int heldBlockLightValue;
uniform int heldItemId;
uniform ivec2 eyeBrightnessSmooth;
uniform mat4 gbufferModelView;
uniform mat4 gbufferModelViewInverse;
uniform sampler2D noisetex;
uniform vec3 actualCameraPosition;

varying float ao;
varying float isLava;
varying vec2 lmcoord;
varying vec2 randCoord;
varying vec2 texcoord;

#ifdef CROSS_PROCESS
	varying vec3 crossProcessColor; //Color to use for cross-processing
#endif

varying vec3 vPosPlayer;
varying vec3 vPosView;
varying vec4 tint;
varying vec4 heldLightColor; //Color of held light source. Alpha = brightness.

const int noiseTextureResolution = 256;
const float invNoiseRes = 1.0 / float(noiseTextureResolution);

const vec3 blocklightVibrantColorFar  = vec3(1.4, 1.0, 0.8); //Vibrant color for block lights when standing far away from a light source.
const vec3 blocklightVibrantColorNear = vec3(1.2, 1.1, 1.0); //Vibrant color for block lights when standing near a light source.

const float lavaOverlayResolution                     = 24.0;

//Overworld cloud colors are multi-dimensional because they're used by /lib/fastDrawClouds.glsl for end portal blocks.

const float dragonEggJumpAttemptsPerSecond = 2.0;
const float dragonEggJumpSuccessChance     = 0.25;
const float dragonEggMinJumpAmount         = 0.25;
const float dragonEggMaxJumpAmount         = 0.5;
const float dragonEggJumpDecaySpeed        = 3.14159265359;
const float dragonEggRotationSpeed         = 4.0;

const vec3 blockVibrantColorNearInSoulSandValleys = vec3(1.0,  1.1, 1.2); //vibrant color for block lights in soul sand valleys when the player is near a light source.
const vec3 blockVibrantColorFarInSoulSandValleys  = vec3(0.8,  1.0, 1.4); //vibrant color for block lights in soul sand valleys when the player is far away from a light source.
const vec3 ambientVibrantColorInSoulSandValleys   = vec3(0.75, 1.0, 1.5);
const vec3 ambientVibrantColorInOtherBiomes       = vec3(1.5,  1.0, 0.75);

#ifdef DYNAMIC_LIGHTS
	float flicker() {
		/*
		#ifdef DYNAMIC_LIGHT_FLICKER
		#endif
		*/
		#if DYNAMIC_LIGHT_FLICKER != 0
			float n = texture2D(noisetex, frameTimeCounter * vec2(16.7825, 15.4192) * invNoiseRes).r - 0.5;
			return n * n * n * DYNAMIC_LIGHT_FLICKER;
		#else
			return 0.0;
		#endif
	}

	vec4 calcHeldLightColor() { //rgb = color, a = brightness
		if (heldBlockLightValue == 0) return vec4(0.0); //not holding a light source
		else if (heldItemId == 50   ) return vec4(1.0,  0.6,  0.3, heldBlockLightValue + flicker()); //regular torches/lanterns/campfires
		else if (heldItemId == 89   ) return vec4(1.0,  0.6,  0.1, heldBlockLightValue            ); //glowstone
		else if (heldItemId == 169  ) return vec4(0.6,  0.8,  0.6, heldBlockLightValue            ); //sea lanterns
		else if (heldItemId == 198  ) return vec4(0.75, 0.55, 0.8, heldBlockLightValue            ); //end rods
		else if (heldItemId == 76   ) return vec4(1.0,  0.3,  0.1, heldBlockLightValue + flicker()); //redstone torches
		else if (heldItemId == 91   ) return vec4(1.0,  0.5,  0.1, heldBlockLightValue + flicker()); //jack-o-lanterns
		else if (heldItemId == 138  ) return vec4(0.4,  0.6,  0.8, heldBlockLightValue            ); //beacons
		#if MC_VERSION >= 11600
		else if (heldItemId == 10001) return vec4(0.3,  0.6,  1.0, heldBlockLightValue + flicker()); //soul torches/lanterns/campfires.
		else if (heldItemId == 10002) return vec4(1.0,  0.5,  0.1, heldBlockLightValue            ); //shroomlight
		else if (heldItemId == 10004) return vec4(0.625 + sin(frameTimeCounter) * 0.125, 0.25, 1.0, heldBlockLightValue); //crying obsidian
		#endif
		#if MC_VERSION >= 11300
		else if (heldItemId == 10003) return vec4(0.9,  1.0,  0.5, heldBlockLightValue            ); //sea pickles
		#endif
		else                          return vec4(0.8,  0.65, 0.5, heldBlockLightValue            ); //everything else
	}
#endif

#if LAVA_WAVE_STRENGTH != 0
	float lavaWave(vec2 pos) {
		pos *= invNoiseRes;
		float offset = 0.875;
		offset += cos(texture2D(noisetex, pos / 30.0).r * 25.0 + frameTimeCounter) * 0.5;
		offset += cos(texture2D(noisetex, pos / 20.0).r * 12.5 + frameTimeCounter * 1.5) * 0.375;
		return offset * (float(LAVA_WAVE_STRENGTH) / 100.0 / 1.75);
	}
#endif

void main() {
	ao = 1.0;
	isLava = 0.0;

	vPosView = (gl_ModelViewMatrix * gl_Vertex).xyz;
	vPosPlayer = mat3(gbufferModelViewInverse) * vPosView;
	vec3 worldPos = vPosPlayer + actualCameraPosition;
	vec3 normal = gl_Normal;

	texcoord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
	lmcoord  = (gl_TextureMatrix[1] * gl_MultiTexCoord1).xy;
	tint = gl_Color;

	//Using IDs above 10000 to represent all blocks that I care about
	//if the ID is less than 10000, then I don't need to do extra logic to see if it has special effects.
	if (mc_Entity.x > 10000.0) {
		int id = int(mc_Entity.x) - 10000;
		if (id == 7) { //lava
			lmcoord.x = 0.96875; //hide vanilla lighting glitches
				isLava = 1.0;

			#if LAVA_WAVE_STRENGTH != 0
				if (worldPos.y <= NETHER_LAVA_LEVEL + 0.01) {
					worldPos.y -= lavaWave(worldPos.xz + 0.5) * fract(worldPos.y - 0.01); // + 0.5 to avoid sharp edges in lava displacement when the coords are on the edge of a noisetex pixel
				}
			#endif

			#ifdef LAVA_PATCHES
				if (abs(normal.y) > 0.1) randCoord = worldPos.xz * 0.5;
				else randCoord = vec2((worldPos.x + worldPos.z) * 4.0, worldPos.y + frameTimeCounter);
			#endif
		}
		/*
		#ifdef LAVA_PATCHES
		#endif
		#ifdef SOUL_LAVA
		#endif
		*/
		#if defined(LAVA_PATCHES) || defined(SOUL_LAVA)
			else if (id == 18) { //magma
				isLava = 1.0;
				randCoord = (
					abs(gl_Normal.y) > 0.1
					? worldPos.xz * 0.5
					: vec2((worldPos.x + worldPos.z) * 4.0, worldPos.y + frameTimeCounter)
				);
			}
		#endif
		else if (id == 2 || id == 3 || id == 4) { //plants and double plants
			normal = vec3(0.0, 1.0, 0.0);

			#ifdef GRASS_AO
				ao = float(texcoord.y < mc_midTexCoord.y);
				if (id != 2) ao = (ao + float(id == 4)) * 0.5;
			#endif

			#ifdef REMOVE_Y_OFFSET
				worldPos.y = floor(worldPos.y + 0.5);
			#endif

			#ifdef REMOVE_XZ_OFFSET
				worldPos.xz = floor(worldPos.xz + 0.5);
			#endif
		}
		#ifdef GRASS_AO
			else if (id == 5) { //crops
				normal = vec3(0.0, 1.0, 0.0);

				ao = float(texcoord.y < mc_midTexCoord.y);
			}
		#endif
		else if (id == 20) { //soul fire
			normal = vec3(0.0, 1.0, 0.0);

			lmcoord.x = 29.0 / 32.0;
		}
		else if (id == 21) { //mushrooms and other sturdy plants
			normal = vec3(0.0, 1.0, 0.0);

			#ifdef GRASS_AO
				//mushrooms are kind of short, so multiply by 2
				//to make the AO start at half the block height.
				ao = float(texcoord.y < mc_midTexCoord.y) * 2.0;
			#endif
		}
		else if (id == 8) { //shadeless blocks
			normal = vec3(0.0, 1.0, 0.0);
		}
		#ifdef JUMPING_DRAGON_EGGS
			else if (id == 19) { //dragon eggs
				float jumpTime = frameTimeCounter * dragonEggJumpAttemptsPerSecond;
				float fracTime = fract(jumpTime);
				vec2 randomPos = vec2(fracTime - jumpTime, floor(jumpTime * invNoiseRes)) + vec2(0.5);
				vec3 jumpRandom = texture2D(noisetex, randomPos * invNoiseRes).rgb;
				if (jumpRandom.y < dragonEggJumpSuccessChance) {
					//find the block that the dragon egg is in.
					//this is possible because none of the dragon egg's
					//vertexes are actually on the edge of the block.
					//however, there are a few on the top and
					//bottom which are on the face of the block.
					//these vertexes are aligned using the
					//vertex's normal vector and texcoord.
					float fracY = fract(worldPos.y);
					vec3 centerPos;
					if (fracY == clamp(fracY, 0.03125, 0.96875)) {
						//normal vertex. position alone is enough to identify the center.
						centerPos = floor(worldPos);
					}
					else if (abs(gl_Normal.y) > 0.9) {
						//top or bottom face. use normals.
						centerPos = worldPos;
						centerPos.y -= gl_Normal.y * 0.25;
						centerPos = floor(centerPos);
					}
					else {
						//side directly connected to top or bottom. use texcoord.
						centerPos = worldPos;
						centerPos.y -= sign(mc_midTexCoord.y - texcoord.y) * 0.25;
						centerPos = floor(centerPos);
					}
					centerPos.xz += vec2(0.5);

					//determine how much the egg should jump, and in which direction.
					float yaw =
						jumpRandom.x * (3.14159265359 * 2.0)
						+ (jumpRandom.z - 0.5) * fracTime * dragonEggRotationSpeed;
					float reverseTime = 1.0 - fracTime;
					float pitch =
						mix(
							dragonEggMinJumpAmount,
							dragonEggMaxJumpAmount,
							jumpRandom.y / dragonEggJumpSuccessChance
						)
						* sin(-dragonEggJumpDecaySpeed * log2(reverseTime))
						* reverseTime;

					//create a rotation matrix to move the egg correctly.
					float x = cos(yaw);
					float z = sin(yaw);
					float xz = x * z;
					//the T in the following variables is short for "theta", which is a greek
					//letter often used in math to describe an angle or an amount of rotation.
					float sinT = sin(pitch);
					float cosT = cos(pitch);
					float invCosT = 1.0 - cosT;
					//this matrix was from the wikipedia page for "rotation matrix". it was then
					//simplified by removing unnecessary multiplications by 0, since y is always 0.
					mat3 jumpMatrix = mat3(
						cosT + x * x * invCosT,
						-z * sinT,
						xz * invCosT,

						z * sinT,
						cosT,
						-x * sinT,
						
						xz * invCosT,
						x * sinT,
						cosT + z * z * invCosT
					);

					//make the egg jump!
					vec3 relativePos = worldPos - centerPos;
					relativePos = jumpMatrix * relativePos;
					worldPos = relativePos + centerPos;

					//rotate normals too, because I can.
					normal = jumpMatrix * normal;
				}
			}
		#endif
	}

	#ifdef DYNAMIC_LIGHTS
		heldLightColor = calcHeldLightColor();
	#endif

	#ifdef CROSS_PROCESS
		/*
		#ifdef SOUL_FIRE_INTEGRATION
		#endif
		*/
		#if defined(SOUL_FIRE_INTEGRATION) && MC_VERSION >= 11600
			#ifdef SOUL_LAVA
				float soul = inSoulSandValley;
			#else
				float soul = smoothstep(NETHER_LAVA_LEVEL, NETHER_LAVA_LEVEL + 16.0, vPosPlayer.y + actualCameraPosition.y) * inSoulSandValley;
			#endif
			vec3 blockCrossColor = mix(
				mix(blocklightVibrantColorFar, blockVibrantColorFarInSoulSandValleys, soul),
				mix(blocklightVibrantColorNear, blockVibrantColorNearInSoulSandValleys, soul),
				eyeBrightnessSmooth.x / 240.0
			);
			vec3 ambientCrossColor = mix(ambientVibrantColorInOtherBiomes, ambientVibrantColorInSoulSandValleys, soul);
			crossProcessColor = mix(ambientCrossColor, blockCrossColor, lmcoord.x); //final cross-processing color
		#else
			vec3 blockCrossColor = mix(blocklightVibrantColorFar, blocklightVibrantColorNear, eyeBrightnessSmooth.x / 240.0);
			crossProcessColor = mix(ambientVibrantColorInOtherBiomes, blockCrossColor, lmcoord.x); //final cross-processing color
		#endif
	#endif

	vPosPlayer = worldPos - actualCameraPosition;
	vPosView = mat3(gbufferModelView) * vPosPlayer;
	gl_Position = gl_ProjectionMatrix * vec4(vPosView, 1.0);

	float glmult = dot(vec4(abs(normal.x), abs(normal.z), max(normal.y, 0.0), max(-normal.y, 0.0)), vec4(0.6, 0.8, 1.0, 0.5));
	glmult = mix(glmult, 1.0, lmcoord.x * lmcoord.x); //increase brightness when block light is high
	tint.rgb *= glmult;
}