
mat4 shadowRotationMatrix = mat4(cos(rotationAngle * PI), sin(rotationAngle * PI), 0, 0,
                                -sin(rotationAngle * PI), cos(rotationAngle * PI), 0, 0,
                                                       0,                       0, 1, 0,
                                                       0,                       0, 0, 1);

mat4 shadowModelViewEnd = shadowModelView * shadowRotationMatrix;

vec4 GetLight(in int LOD, in vec2 offset, in float range, in float quality, vec3 noisePattern, out MaterialMask mask)
{

	float scale = pow(2.0f, float(LOD));

	float padding = 0.002f;

	if (	texcoord.s - offset.s + padding < 1.0f / scale + (padding * 2.0f)
		&&  texcoord.t - offset.t + padding < 1.0f / scale + (padding * 2.0f)
		&&  texcoord.s - offset.s + padding > 0.0f
		&&  texcoord.t - offset.t + padding > 0.0f)
	{

		vec2 coord = (texcoord.st - offset.st) * scale;

		mask = CalculateMasks(texture(colortex6, coord.st).b);

		if(mask.sky == 0.0){

			vec3 normal 				= GetNormals(coord.st, mask);						//Gets the screen-space normals

			vec4 gn = gbufferModelViewInverse * vec4(normal.xyz, 0.0f);
				 gn = shadowModelViewEnd * gn;
				 gn.xyz = normalize(gn.xyz);

			vec3 shadowSpaceNormal = gn.xyz;

			vec4 screenSpacePosition 	= GetScreenSpacePosition(coord.st, mask); 			//Gets the screen-space position
			vec3 viewVector 			= normalize(screenSpacePosition.xyz);


			float distance = length(screenSpacePosition);

			//float materialIDs = texture(colortex5, coord).b * 255.0f;

			vec4 upVectorShadowSpace = shadowModelViewEnd * vec4(0.0f, 1.0, 0.0, 0.0);

			vec4 worldposition = gbufferModelViewInverse * screenSpacePosition;		//Transform from screen space to world space
				 worldposition = WorldToShadowProjPos(worldposition);

			float shadowMult = 0.0f;														//Multiplier used to fade out shadows at distance
			float shad = 0.0f;
			vec3 fakeIndirect = vec3(0.0f);

			float fakeLargeAO = 0.0;


			float mcSkylight = GetSkylight(coord) * 0.8 + 0.2;

			float fademult = 0.15f;

			if (shadowDistanceRenderMul < 0.0) {
				shadowMult = 1.0;
			}else{
				shadowMult = clamp((shadowDistance * 1.4f * fademult) - (distance * fademult), 0.0f, 1.0f);	//Calculate shadowMult to fade shadows out
			}

			worldposition.z -= 0.002;

			float compare = sin(frameTimeCounter) > -0.2 ? 1.0 : 0.0;

			if (shadowMult > 0.0)
			{


				//big shadow
				float rad = range;

				int c = 0;
				float s = 2.0f * (rad / 2048.0f);

				vec2 dither = noisePattern.xy;

				float step = 1.0f / quality;

				for (float i = -2.0f; i <= 2.0f; i += step) {
					for (float j = -2.0f; j <= 2.0f; j += step) {

						vec2 offset = (vec2(i, j) + dither * step) * s;

						offset *= length(offset) * 15.0;
						offset *= GI_RADIUS * 1.0;


						vec2 coord =  worldposition.st + offset;
						vec2 lookupCoord = DistortShadowSpace(coord);

						float depthSample = textureLod(shadowtex1, lookupCoord, 0).x;


						depthSample = -3 + 5.0 * depthSample;
						vec3 samplePos = vec3(coord.x, coord.y, depthSample);


							fakeLargeAO += saturate((worldposition.z - samplePos.z) * 1000.0);


						vec3 lightVector = normalize(samplePos.xyz - worldposition.xyz);

						vec4 normalSample = textureLod(shadowcolor1, lookupCoord, 6);

						vec3 surfaceNormal = normalSample.rgb * 2.0f - 1.0f;
							 surfaceNormal.x = -surfaceNormal.x;
							 surfaceNormal.y = -surfaceNormal.y;

						float surfaceSkylight = normalSample.a;

						if (surfaceSkylight < 0.2)
						{
							surfaceSkylight = mcSkylight;
						}

						float NdotL = max(0.0f, dot(shadowSpaceNormal.xyz, lightVector * vec3(1.0, 1.0, -1.0)));
							   NdotL = NdotL * 0.9f + 0.2f;

						if (mask.leaves > 0.5 || mask.grass > 0.5)
						{
							NdotL = 0.5f;
						}

						if (NdotL > 0.0)
						{
							bool isTranslucent = length(surfaceNormal) < 0.5f;

							if (isTranslucent)
							{
								surfaceNormal.xyz = vec3(0.0f, 0.0f, 1.0f);
							}



							float weight = dot(lightVector, surfaceNormal);
							float rawdot = weight;

							if (isTranslucent)
							{
								weight = abs(weight) * 0.85f;
							}

							if (normalSample.a < 0.2)
							{
								weight = 0.5;
							}

							weight = max(weight, 0.0f);

							float dist = length(samplePos.xyz - worldposition.xyz - vec3(0.0f, 0.0f, 0.0f));
							if (dist < 0.0005f)
							{
								dist = 10000000.0f;
							}

							const float falloffPower = 1.9f; //Default: 1.9f
							float distanceWeight = (1.0f / (pow(dist * (62260.0f / rad), falloffPower) + 100.1f));
								  distanceWeight *= pow(length(offset), 2.0) * 50000.0 + 1.01;


							//Leaves self-occlusion
							if (rawdot < 0.0f)
							{
								distanceWeight = max(distanceWeight * 30.0f - 0.13f, 0.0f);
								distanceWeight *= 0.04f;
							}


							float skylightWeight = 1.0 / (max(0.0, surfaceSkylight - mcSkylight) * 15.0 + 1.0);



							vec3 colorSample = pow(textureLod(shadowcolor0, lookupCoord, 6).rgb, vec3(2.2f));


							colorSample /= surfaceSkylight;

							colorSample = normalize(colorSample) * pow(length(colorSample), 1.1f);

							fakeIndirect += colorSample * weight * distanceWeight * NdotL * skylightWeight;
						}
						c += 1;
					}
				}

				fakeIndirect /= c;
				fakeLargeAO /= c;
				fakeLargeAO = clamp(1.0 - fakeLargeAO * 0.8, 0.0, 1.0);
			}

			fakeIndirect = mix(vec3(0.0f), fakeIndirect, vec3(shadowMult));


			float ao = 1.0f;
			//bool isSky = GetSkyMask(coord.st);
			#ifdef ENABLE_SSAO
			if (mask.sky == 0.0)
			{
				ao *= GetAO(coord.st, normal.xyz, noisePattern.x, mask);
			}


			#endif
			fakeIndirect.rgb *= ao;


			fakeIndirect.rgb = mix(fakeIndirect.rgb, vec3(Luminance(fakeIndirect.rgb)), vec3(1.0 - GI_SATURATION));

			return vec4(fakeIndirect.rgb * GI_BRIGHTNESS * GI_RADIUS, ao);
		}
		else {
			return vec4(0.0f);
		}
	}
}