//------------------------------------------------------------------------------------------------//
// Объемный туман для Нижнего мира (Nether)
//------------------------------------------------------------------------------------------------//

#define NETHER_FOG_STRENGTH 0.15 // [0.05 0.1 0.15 0.2 0.25 0.3 0.4 0.5]
#define NETHER_FOG_DENSITY 0.8 // [0.3 0.5 0.8 1.0 1.5 2.0 3.0]

//------------------------------------------------------------------------------------------------//

#include "/lib/Lighting/ShadowDistortion.glsl"

vec3 WorldPosToShadowPos(in vec3 worldPos) {
	vec3 shadowClipPos = transMAD(shadowModelView, worldPos);
	shadowClipPos = projMAD(shadowProjection, shadowClipPos);
	shadowClipPos.z *= 0.2;

	return shadowClipPos * 0.5 + 0.5;
}

vec2 DistortShadowSpace(in vec2 shadowClipPos) {
	shadowClipPos = shadowClipPos * 2.0 - 1.0;
	shadowClipPos.xy *= rcp(DistortionFactor(shadowClipPos.xy));

	return shadowClipPos * 0.5 + 0.5;
}

// Анимация тумана
vec3 netherFogWind = vec3(1.0, 0.2, 0.5) * worldTimeCounter * 0.008;

// Расчет плотности тумана для Незера
float CalculateNetherFogDensity(in vec3 rayPosition) {
	// Вертикальная плотность - больше тумана на определенных высотах
	float heightFalloff = exp2(-abs(rayPosition.y - 64.0) * 0.012);
	
	// Добавляем слои тумана на разных высотах
	heightFalloff += exp2(-abs(rayPosition.y - 32.0) * 0.015) * 0.6;
	heightFalloff += exp2(-abs(rayPosition.y - 96.0) * 0.010) * 0.4;
	
	rayPosition *= 0.012;
	rayPosition += netherFogWind;
	
	// Многослойный шум для объемных облаков
	float noise = Get3DNoiseSmooth(rayPosition) * 0.5;
		rayPosition += netherFogWind * 0.5;
	noise += Get3DNoiseSmooth(rayPosition * 2.8) * 0.25;
		rayPosition += netherFogWind * 0.3;
	noise += Get3DNoiseSmooth(rayPosition * 7.5) * 0.125;
		rayPosition += netherFogWind * 0.2;
	noise += Get3DNoiseSmooth(rayPosition * 18.0) * 0.0625;
	
	// Комбинируем шум с высотой
	float density = saturate(heightFalloff * noise * 50.0 - 6.0) * NETHER_FOG_DENSITY;
	
	return density * 0.25;
}

// Цвет тумана в зависимости от биома
vec3 GetNetherFogColor() {
	// Используем те же uniform'ы что и в NetherFog
	vec3 baseColor = vec3(0.99, 0.23, 0.03) * BiomeNetherWastesSmooth
				   + vec3(0.04, 0.24, 0.2) * BiomeWarpedForestSmooth
				   + vec3(0.3, 0.03, 0.01) * BiomeCrimsonForestSmooth
				   + vec3(0.012, 0.055, 0.06) * BiomeSoulSandValleySmooth
				   + vec3(0.5) * BiomeBasaltDeltasSmooth;
	
	// Делаем цвет более насыщенным для объемного тумана
	return baseColor * 1.5;
}

const int shadowMapResolution = 2048;
const float realShadowMapRes = shadowMapResolution * MC_SHADOW_QUALITY;

// Основная функция расчета объемного тумана для Незера
vec4 CalculateNetherVolumetricFog(in vec3 worldPos, in vec3 worldDir, in float dither) {	
	float rayLength = min(800.0, length(worldPos));

	uint steps = uint(14.0 + rayLength * 0.08);
	     steps = min(steps, 24u);

	float rSteps = 1.0 / float(steps);

	float stepLength = rayLength * rSteps;
	float transmittance = 1.0;
	float LdotV = dot(worldLightVector, worldDir);

	// Фазовая функция для рассеивания света
	float airDensity = RayleighPhase(LdotV) * 0.3 * exp2(-abs(worldDir.y) * 1.5) / far;

	vec3 rayStep = worldDir * stepLength;
	vec3 rayPosition = rayStep * dither + gbufferModelViewInverse[3].xyz + cameraPosition;

	vec3 shadowStart = WorldPosToShadowPos(gbufferModelViewInverse[3].xyz);
	vec3 shadowEnd = WorldPosToShadowPos(rayStep + gbufferModelViewInverse[3].xyz);

	vec3 shadowStep = shadowEnd - shadowStart;
	vec3 shadowPosition = shadowStep * dither + shadowStart;
	
	vec3 scatteringSample = vec3(0.0);

	// Фазовые функции Henyey-Greenstein для разного рассеивания
	float phases1 = (HenyeyGreensteinPhase(LdotV, 0.6) + HenyeyGreensteinPhase(LdotV, -0.4)) * 0.5;
	float phases2 = (HenyeyGreensteinPhase(LdotV * 0.5, 0.6) + HenyeyGreensteinPhase(LdotV * 0.5, -0.4)) * 0.25;

	uint i = 0u;
	while (++i < steps) {
		rayPosition += rayStep;
		shadowPosition += shadowStep;

		vec2 shadowProjPos = DistortShadowSpace(shadowPosition.xy);
		if (saturate(shadowProjPos) != shadowProjPos) continue;
		ivec2 shadowTexel = ivec2(shadowProjPos * realShadowMapRes);
	
		float fogDensity = airDensity;
		float density = CalculateNetherFogDensity(rayPosition);
		fogDensity += density;

		if (fogDensity < 1e-5) continue;
		fogDensity *= stepLength;

		// Тени
		vec3 shadow = texelFetch(shadowtex1, shadowTexel, 0).xxx;
		shadow = step(shadowPosition.z, shadow);

		// Самозатенение тумана
		if (density > 1e-5) {
			float stepLength = 4.0;
			float sunlightOD = 0.0;
			vec3 checkPos = rayPosition;
			
			for (uint j = 0u; j < 4u; ++j, checkPos += worldLightVector * stepLength) {
				float checkDensity = CalculateNetherFogDensity(checkPos);
				if (checkDensity < 1e-5) continue;
				sunlightOD += checkDensity * stepLength;
				stepLength *= 1.4;
			}

			float scatteringSun = expf(-sunlightOD * 2.5) * phases1
							    + expf(-sunlightOD * 1.2) * phases2;
			
			shadow *= scatteringSun * 3.0;
		}

		float stepTransmittance = expf(-fogDensity);
		float fogSample = transmittance * oneMinus(stepTransmittance);
		
		scatteringSample += shadow * fogSample;
		transmittance *= stepTransmittance;

		if (transmittance < 1e-3) break;
	}

	// Применяем цвет тумана в зависимости от биома
	vec3 fogColor = GetNetherFogColor() * scatteringSample * NETHER_FOG_STRENGTH * 12.0;

	return vec4(fogColor, transmittance);
}