//////////////////////////////////////////////////////////////////////////////
//    ##      #####   ######   ######     ##     ####     #######  ##  ##   //
//   ####    ##   ##  # ## #    ##  ##   ####     ##       ##   #  ##  ##   //
//  ##  ##   #          ##      ##  ##  ##  ##    ##       ## #     ####    //
//  ##  ##    #####     ##      #####   ##  ##    ##       ####      ##     //
//  ######        ##    ##      ## ##   ######    ##   #   ## #     ####    //
//  ##  ##   ##   ##    ##      ##  ##  ##  ##    ##  ##   ##   #  ##  ##   //
//  ##  ##    #####    ####    #### ##  ##  ##   #######  #######  ##  ##   //
//--------------------------------------------------------------------------//
//--------------------------------------------------------------------------//
//      #####   ##   ##    ##     #####    #######  ######    #####         //
//     ##   ##  ##   ##   ####     ## ##    ##   #   ##  ##  ##   ##        //
//     #        ##   ##  ##  ##    ##  ##   ## #     ##  ##  #              //
//      #####   #######  ##  ##    ##  ##   ####     #####    #####         //
//          ##  ##   ##  ######    ##  ##   ## #     ## ##        ##        //
//          ##  ##   ##  ######    ##  ##   ## #     ## ##        ##        //
//     ##   ##  ##   ##  ##  ##    ## ##    ##   #   ##  ##  ##   ##        //
//      #####   ##   ##  ##  ##   #####    #######  #### ##   #####         //
/////(BSL Shaders Edit)//////////////////////////////////////By LexBoosT//////

float distx(float dist){
	return(far*(dist-near))/(dist*(far-near));
}

float getDepth(float depth) {
    return 2.0 * near * far / (far + near - (2.0 * depth - 1.0) * (far - near));
}

vec4 distortShadow(vec4 shadowpos, float distortFactor) {
	shadowpos.xy *= 1.0 / distortFactor;
	shadowpos.z = shadowpos.z * 0.2;
	shadowpos = shadowpos * 0.5 + 0.5;

	return shadowpos;
}

vec4 GetWorldSpace(float shadowdepth, vec2 texCoord) {
	vec4 viewPos = gbufferProjectionInverse * (vec4(texCoord, shadowdepth, 1.0) * 2.0 - 1.0);
	viewPos /= viewPos.w;

	vec4 wpos = gbufferModelViewInverse * viewPos;
	wpos /= wpos.w;
	
	return wpos;
}

vec4 getShadowSpace(vec4 wpos) {
	wpos = shadowModelView * wpos;
	wpos = shadowProjection * wpos;
	wpos /= wpos.w;

	float distb = sqrt(wpos.x * wpos.x + wpos.y * wpos.y);
	float distortFactor = 1.0 - shadowMapBias + distb * shadowMapBias;
	wpos = distortShadow(wpos,distortFactor);
	
	#if defined OVERWORLD && defined WATER_CAUSTICS && defined SMOKE_WATER_LIGHTSHAFTS
		if (isEyeInWater == 1.0) {
			vec3 worldPos = ToWorld(wpos.xyz);
			vec3 causticpos = worldPos.xyz + cameraPosition.xyz;
			float caustic = getCausticWaves(causticpos.xyz * 0.25);
			wpos.xy *= 1.0 + caustic * 0.0125;
		}
	#endif
	
	return wpos;
}

vec3 getVolumetricRays(float pixeldepth0, float pixeldepth1, vec3 color, float dither, vec4 viewPos, vec3 nViewPos) {
	vec3 vl = vec3(0.0);

	#if AA > 0
		dither = fract(dither + frameTimeCounter * 64.0);
	#endif

	#ifdef OVERWORLD
	vec3 lightVec = sunVec * ((timeAngle < 0.5325 || timeAngle > 0.9675) ? 1.0 : -1.0);
	#else
	vec3 lightVec = sunVec;
	#endif

	#ifdef OVERWORLD

			float VoL = dot(nViewPos, lightVec);

			#ifdef END
			float visfactor = 0.01 * clamp((3.0 * max(rainStrengthS - isEyeInWater, 0.0) + 1.0),0.0,1.0);
			float invvisfactor = 1.0 - visfactor;

			float visibility = clamp(VoL * 0.5 + 0.5, 0.0, 1.0);
				  visibility = clamp((visfactor / (1.0 - invvisfactor * visibility) - visfactor) * 0.015 / invvisfactor - 0.015, 0.0, 1.0);
				  visibility = visibility * 0.14285;

			#else

			float visibility = 0.055;

			if (moonVisibility > 0.0){
			if (isEyeInWater == 1) visibility += 0.50;
			}
			if (sunVisibility > 0.0){
			if (isEyeInWater == 1) visibility = 0.07;
			}

			float endurance = LIGHTSHAFT_ENDURANCE;
			if (isEyeInWater == 0) endurance *= min(2.0 + pow2(rainStrengthS)- pow2(sunVisibility), 2.0);
			else visibility *= 1.0 + 2.0 * pow(max(VoL, 0.0), 128.0) * float(sunVisibility > 0.5);

			if (endurance >= 1.0) visibility *= max((VoL + endurance) / (endurance + 1.0), 0.0);
				else visibility *= pow(max((VoL + 1.0) / 2.0, 0.0), (11.0 - endurance * 10.0));

			#endif	

			visibility *= 1.0 - isEyeInCave;

	#endif

	#ifdef END
	#ifdef LIGHTSHAFT_END
	float visibility=.14285* float(pixeldepth0 > 0.56);
	#else
	float visibility=0;
	#endif
	#endif

	if (visibility > 0.0) {
		vec4 shadowposition = vec4(0.0);

		#ifdef END
			float maxDist = 288.0 * (1.5 - isEyeInWater);
		#else
			float maxDist = 288.0 * 2.0 * (1.5);

			if (isEyeInWater == 1) 
			maxDist = min(192.0, shadowDistance * 0.75);
		#endif
		
		float depth0 = getDepth(pixeldepth0);
		float depth1 = getDepth(pixeldepth1);
		vec4 worldposition = vec4(0.0);
		
		vec3 watercol = rawWaterColor.rgb / UNDERWATER_I;
		watercol = pow(watercol * 1.0, vec3(2.2)) * 70.0;

		vec3 watercol2 = rawWaterColorLightshaft.rgb / UNDERWATER_LIGHTSHAFT_I;
		watercol2 = pow(watercol2 * 1.0, vec3(2.2)) * 70.0;

		#ifdef END
			float minDistFactor =11.0;
		#else
			float minDistFactor =11.0;

			if (moonVisibility >0.0){
			minDistFactor *= clamp(far, 288.0, 512.0) / 192.0;
			}
			if (sunVisibility >0.0){
			minDistFactor *= clamp(far, 256.0, 512.0) / 192.0;
			}


			float fovFactor = gbufferProjection[1][1] / 1.37;
			float x = abs(texCoord.x - 0.5);
			x = 1.0 - x*x;
			x = pow(x, max(3.0 - fovFactor, 0.0));
			minDistFactor *= x;
			maxDist *= x;
		#endif

		#ifdef END
			int sampleCount = LIGHTSHAFT_SAMPLE_COUNT;
		#else
			int sampleCount = LIGHTSHAFT_SAMPLE_COUNT;
		#endif

		for(int i = 0; i < sampleCount; i++) {
			#ifdef END
				float minDist = exp2(i + dither) - 0.9;
			#else
				float minDist = (pow(i + dither + 0.1, 2.0)) * minDistFactor * 0.055;
				if (isEyeInWater == 1) minDist = (pow(i + dither + 0.3, 2.0)) * minDistFactor * 0.055;
			#endif

			float breakFactor = 0.0;
			if (minDist >= maxDist) breakFactor = 1.0;
			if (breakFactor > 0.5) break;

			if (depth1 < minDist || (depth0 < minDist && color == vec3(0.0))) break;

			worldposition = GetWorldSpace(distx(minDist), texCoord.st);
			shadowposition = getShadowSpace(worldposition);
			shadowposition.z += 0.0512 / shadowMapResolution;

			if (length(shadowposition.xy * 2.0 - 1.0) < 1.0)	{
				vec3 shadow0 = vec3(shadow2D(shadowtex0, shadowposition.xyz).z);
				
				vec3 shadowCol = vec3(0.0);
				#ifdef SHADOW_COLOR
					if (shadow0.r < 0.9) {
						float shadow1 = shadow2D(shadowtex1, shadowposition.xyz).z;
						if (shadow1 > 0.9) {
							shadowCol = texture2D(shadowcolor0, shadowposition.xy).rgb;
							shadowCol *= shadowCol * shadow1 ;
							shadow0 = shadowCol * (1.0 - shadow0) + shadow0;
						}
					}
				#endif
				if (depth0 < minDist) shadow0 *= color;

				#if defined END && defined LIGHTSHAFT_END
					#ifdef LIGHT_SHAFT_SMOKER
					vec3 npos = worldposition.xyz + cameraPosition.xyz + vec3(frameTimeCounter * CLOUD_SPEED_END*0.25, 0, 0);
					float n3da = texture2D(noisetex, npos.xz / 512.0 + floor(npos.y / 3.0) * 0.35).r;
					float n3db = texture2D(noisetex, npos.xz / 512.0 + floor(npos.y / 3.0 + 1.0) * 0.35).r;
					float noise = mix(n3da, n3db, fract(npos.y / 3.0));
					noise = sin(noise * 28.0 + frameTimeCounter *CLOUD_SPEED_END*0.25) * 0.25 + 0.5;
					shadow0 *= noise;
					#endif
				#endif
				
				#if defined OVERWORLD && defined LIGHT_SHAFT
					#if SMOKER_LIGHT_SHAFTS > 0

						vec3 npos = worldposition.xyz + cameraPosition.xyz + vec3(frameTimeCounter * LIGHT_SHAFT_SMOKER_OVERWORLD_SPEED*0.25, 0, 0);
						float n3da = texture2D(noisetex, npos.xz /1024.0 + floor(npos.y / 3.0) * 0.85).r;
						float n3db = texture2D(noisetex, npos.xz / 1024.0 + floor(npos.y / 3.0 + 1.0) * 0.85).r;
						float noise = mix(n3da, n3db, fract(npos.y / 3.0));
						noise = sin(noise * 25 + frameTimeCounter *LIGHT_SHAFT_SMOKER_OVERWORLD_SPEED*0.25) * 0.25 + 0.5;
						#if SMOKER_LIGHT_SHAFTS == 1
						noise = mix(1.0,noise,moonVisibility);
						#endif
						#if SMOKER_LIGHT_SHAFTS == 2
						noise = mix(1.0,noise,rainStrengthS);
						#endif
						#if SMOKER_LIGHT_SHAFTS == 3
						noise = mix(1.0,noise,1.0-((1.0-rainStrengthS)*(1.0-moonVisibility)));
						#endif
						shadow0 *= noise;
						
					#endif
				#endif
				
				#ifdef END
					if (isEyeInWater == 1) shadow0 *= watercol.rgb;
					vl += shadow0;
				#else
					if (isEyeInWater == 0) {
					vl += shadow0 * LIGHTSHAFT_SAMPLE_INTENSITY * 0.25;
					}else{
					shadow0 *= watercol2 * watercol2 * watercol2;
					float sampleFactor = sqrt(minDist / maxDist);
					vl += shadow0 * sampleFactor* 0.75;
					}
				#endif
			} else {
				vl += 1.0;
			}
			if (breakFactor > 0.5) break;
		}
		#if SHADOWS_LEAKS == 1
		vl = vl * visibility;
		#endif
		#if SHADOWS_LEAKS == 2
		vl = sqrt(vl * visibility);
		#endif

				if (isEyeInWater == 0) vl = clamp(pow(vl, vec3(1.25 + 0.75 * (sunVisibility * 0.5 + 0.5) * (1.0 - rainStrengthS))),0.0,1.0);

		if(isEyeInWater==1)	{
			vl /= sqrt(vl);
		}
		vl *= 0.9;
		if (dot(vl, vl) > 0.0) vl += (dither - 0.25) / 256.0;
	}

	return vl;
}