package net.minecraft.client.renderer; import com.google.common.collect.Lists; import com.mojang.blaze3d.shaders.FogShape; import com.mojang.blaze3d.systems.RenderSystem; import java.util.List; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.tags.BiomeTags; import net.minecraft.util.CubicSampler; import net.minecraft.util.Mth; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.material.FogType; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; @Environment(EnvType.CLIENT) public class FogRenderer { private static final int WATER_FOG_DISTANCE = 96; private static final List MOB_EFFECT_FOG = Lists.newArrayList( new FogRenderer.BlindnessFogFunction(), new FogRenderer.DarknessFogFunction() ); public static final float BIOME_FOG_TRANSITION_TIME = 5000.0F; private static float fogRed; private static float fogGreen; private static float fogBlue; private static int targetBiomeFog = -1; private static int previousBiomeFog = -1; private static long biomeChangedTime = -1L; public static void setupColor(Camera activeRenderInfo, float partialTicks, ClientLevel level, int renderDistanceChunks, float bossColorModifier) { FogType fogType = activeRenderInfo.getFluidInCamera(); Entity entity = activeRenderInfo.getEntity(); if (fogType == FogType.WATER) { long l = Util.getMillis(); int i = level.getBiome(BlockPos.containing(activeRenderInfo.getPosition())).value().getWaterFogColor(); if (biomeChangedTime < 0L) { targetBiomeFog = i; previousBiomeFog = i; biomeChangedTime = l; } int j = targetBiomeFog >> 16 & 0xFF; int k = targetBiomeFog >> 8 & 0xFF; int m = targetBiomeFog & 0xFF; int n = previousBiomeFog >> 16 & 0xFF; int o = previousBiomeFog >> 8 & 0xFF; int p = previousBiomeFog & 0xFF; float f = Mth.clamp((float)(l - biomeChangedTime) / 5000.0F, 0.0F, 1.0F); float g = Mth.lerp(f, (float)n, (float)j); float h = Mth.lerp(f, (float)o, (float)k); float q = Mth.lerp(f, (float)p, (float)m); fogRed = g / 255.0F; fogGreen = h / 255.0F; fogBlue = q / 255.0F; if (targetBiomeFog != i) { targetBiomeFog = i; previousBiomeFog = Mth.floor(g) << 16 | Mth.floor(h) << 8 | Mth.floor(q); biomeChangedTime = l; } } else if (fogType == FogType.LAVA) { fogRed = 0.6F; fogGreen = 0.1F; fogBlue = 0.0F; biomeChangedTime = -1L; } else if (fogType == FogType.POWDER_SNOW) { fogRed = 0.623F; fogGreen = 0.734F; fogBlue = 0.785F; biomeChangedTime = -1L; RenderSystem.clearColor(fogRed, fogGreen, fogBlue, 0.0F); } else { float r = 0.25F + 0.75F * renderDistanceChunks / 32.0F; r = 1.0F - (float)Math.pow(r, 0.25); Vec3 vec3 = level.getSkyColor(activeRenderInfo.getPosition(), partialTicks); float s = (float)vec3.x; float t = (float)vec3.y; float u = (float)vec3.z; float v = Mth.clamp(Mth.cos(level.getTimeOfDay(partialTicks) * (float) (Math.PI * 2)) * 2.0F + 0.5F, 0.0F, 1.0F); BiomeManager biomeManager = level.getBiomeManager(); Vec3 vec32 = activeRenderInfo.getPosition().subtract(2.0, 2.0, 2.0).scale(0.25); Vec3 vec33 = CubicSampler.gaussianSampleVec3( vec32, (ix, j, k) -> level.effects().getBrightnessDependentFogColor(Vec3.fromRGB24(biomeManager.getNoiseBiomeAtQuart(ix, j, k).value().getFogColor()), v) ); fogRed = (float)vec33.x(); fogGreen = (float)vec33.y(); fogBlue = (float)vec33.z(); if (renderDistanceChunks >= 4) { float f = Mth.sin(level.getSunAngle(partialTicks)) > 0.0F ? -1.0F : 1.0F; Vector3f vector3f = new Vector3f(f, 0.0F, 0.0F); float h = activeRenderInfo.getLookVector().dot(vector3f); if (h < 0.0F) { h = 0.0F; } if (h > 0.0F) { float[] fs = level.effects().getSunriseColor(level.getTimeOfDay(partialTicks), partialTicks); if (fs != null) { h *= fs[3]; fogRed = fogRed * (1.0F - h) + fs[0] * h; fogGreen = fogGreen * (1.0F - h) + fs[1] * h; fogBlue = fogBlue * (1.0F - h) + fs[2] * h; } } } fogRed = fogRed + (s - fogRed) * r; fogGreen = fogGreen + (t - fogGreen) * r; fogBlue = fogBlue + (u - fogBlue) * r; float fx = level.getRainLevel(partialTicks); if (fx > 0.0F) { float g = 1.0F - fx * 0.5F; float hx = 1.0F - fx * 0.4F; fogRed *= g; fogGreen *= g; fogBlue *= hx; } float g = level.getThunderLevel(partialTicks); if (g > 0.0F) { float hx = 1.0F - g * 0.5F; fogRed *= hx; fogGreen *= hx; fogBlue *= hx; } biomeChangedTime = -1L; } float rx = ((float)activeRenderInfo.getPosition().y - level.getMinBuildHeight()) * level.getLevelData().getClearColorScale(); FogRenderer.MobEffectFogFunction mobEffectFogFunction = getPriorityFogFunction(entity, partialTicks); if (mobEffectFogFunction != null) { LivingEntity livingEntity = (LivingEntity)entity; rx = mobEffectFogFunction.getModifiedVoidDarkness(livingEntity, livingEntity.getEffect(mobEffectFogFunction.getMobEffect()), rx, partialTicks); } if (rx < 1.0F && fogType != FogType.LAVA && fogType != FogType.POWDER_SNOW) { if (rx < 0.0F) { rx = 0.0F; } rx *= rx; fogRed *= rx; fogGreen *= rx; fogBlue *= rx; } if (bossColorModifier > 0.0F) { fogRed = fogRed * (1.0F - bossColorModifier) + fogRed * 0.7F * bossColorModifier; fogGreen = fogGreen * (1.0F - bossColorModifier) + fogGreen * 0.6F * bossColorModifier; fogBlue = fogBlue * (1.0F - bossColorModifier) + fogBlue * 0.6F * bossColorModifier; } float sx; if (fogType == FogType.WATER) { if (entity instanceof LocalPlayer) { sx = ((LocalPlayer)entity).getWaterVision(); } else { sx = 1.0F; } } else if (entity instanceof LivingEntity livingEntity2 && livingEntity2.hasEffect(MobEffects.NIGHT_VISION) && !livingEntity2.hasEffect(MobEffects.DARKNESS)) { sx = GameRenderer.getNightVisionScale(livingEntity2, partialTicks); } else { sx = 0.0F; } if (fogRed != 0.0F && fogGreen != 0.0F && fogBlue != 0.0F) { float tx = Math.min(1.0F / fogRed, Math.min(1.0F / fogGreen, 1.0F / fogBlue)); fogRed = fogRed * (1.0F - sx) + fogRed * tx * sx; fogGreen = fogGreen * (1.0F - sx) + fogGreen * tx * sx; fogBlue = fogBlue * (1.0F - sx) + fogBlue * tx * sx; } RenderSystem.clearColor(fogRed, fogGreen, fogBlue, 0.0F); } public static void setupNoFog() { RenderSystem.setShaderFogStart(Float.MAX_VALUE); } @Nullable private static FogRenderer.MobEffectFogFunction getPriorityFogFunction(Entity entity, float partialTick) { return entity instanceof LivingEntity livingEntity ? (FogRenderer.MobEffectFogFunction)MOB_EFFECT_FOG.stream() .filter(mobEffectFogFunction -> mobEffectFogFunction.isEnabled(livingEntity, partialTick)) .findFirst() .orElse(null) : null; } public static void setupFog(Camera camera, FogRenderer.FogMode fogMode, float farPlaneDistance, boolean shouldCreateFog, float partialTick) { FogType fogType = camera.getFluidInCamera(); Entity entity = camera.getEntity(); FogRenderer.FogData fogData = new FogRenderer.FogData(fogMode); FogRenderer.MobEffectFogFunction mobEffectFogFunction = getPriorityFogFunction(entity, partialTick); if (fogType == FogType.LAVA) { if (entity.isSpectator()) { fogData.start = -8.0F; fogData.end = farPlaneDistance * 0.5F; } else if (entity instanceof LivingEntity && ((LivingEntity)entity).hasEffect(MobEffects.FIRE_RESISTANCE)) { fogData.start = 0.0F; fogData.end = 5.0F; } else { fogData.start = 0.25F; fogData.end = 1.0F; } } else if (fogType == FogType.POWDER_SNOW) { if (entity.isSpectator()) { fogData.start = -8.0F; fogData.end = farPlaneDistance * 0.5F; } else { fogData.start = 0.0F; fogData.end = 2.0F; } } else if (mobEffectFogFunction != null) { LivingEntity livingEntity = (LivingEntity)entity; MobEffectInstance mobEffectInstance = livingEntity.getEffect(mobEffectFogFunction.getMobEffect()); if (mobEffectInstance != null) { mobEffectFogFunction.setupFog(fogData, livingEntity, mobEffectInstance, farPlaneDistance, partialTick); } } else if (fogType == FogType.WATER) { fogData.start = -8.0F; fogData.end = 96.0F; if (entity instanceof LocalPlayer localPlayer) { fogData.end = fogData.end * Math.max(0.25F, localPlayer.getWaterVision()); Holder holder = localPlayer.level().getBiome(localPlayer.blockPosition()); if (holder.is(BiomeTags.HAS_CLOSER_WATER_FOG)) { fogData.end *= 0.85F; } } if (fogData.end > farPlaneDistance) { fogData.end = farPlaneDistance; fogData.shape = FogShape.CYLINDER; } } else if (shouldCreateFog) { fogData.start = farPlaneDistance * 0.05F; fogData.end = Math.min(farPlaneDistance, 192.0F) * 0.5F; } else if (fogMode == FogRenderer.FogMode.FOG_SKY) { fogData.start = 0.0F; fogData.end = farPlaneDistance; fogData.shape = FogShape.CYLINDER; } else { float f = Mth.clamp(farPlaneDistance / 10.0F, 4.0F, 64.0F); fogData.start = farPlaneDistance - f; fogData.end = farPlaneDistance; fogData.shape = FogShape.CYLINDER; } RenderSystem.setShaderFogStart(fogData.start); RenderSystem.setShaderFogEnd(fogData.end); RenderSystem.setShaderFogShape(fogData.shape); } public static void levelFogColor() { RenderSystem.setShaderFogColor(fogRed, fogGreen, fogBlue); } @Environment(EnvType.CLIENT) static class BlindnessFogFunction implements FogRenderer.MobEffectFogFunction { @Override public Holder getMobEffect() { return MobEffects.BLINDNESS; } @Override public void setupFog(FogRenderer.FogData fogData, LivingEntity entity, MobEffectInstance effectInstance, float farPlaneDistance, float f) { float g = effectInstance.isInfiniteDuration() ? 5.0F : Mth.lerp(Math.min(1.0F, effectInstance.getDuration() / 20.0F), farPlaneDistance, 5.0F); if (fogData.mode == FogRenderer.FogMode.FOG_SKY) { fogData.start = 0.0F; fogData.end = g * 0.8F; } else { fogData.start = g * 0.25F; fogData.end = g; } } } @Environment(EnvType.CLIENT) static class DarknessFogFunction implements FogRenderer.MobEffectFogFunction { @Override public Holder getMobEffect() { return MobEffects.DARKNESS; } @Override public void setupFog(FogRenderer.FogData fogData, LivingEntity entity, MobEffectInstance effectInstance, float farPlaneDistance, float f) { float g = Mth.lerp(effectInstance.getBlendFactor(entity, f), farPlaneDistance, 15.0F); fogData.start = fogData.mode == FogRenderer.FogMode.FOG_SKY ? 0.0F : g * 0.75F; fogData.end = g; } @Override public float getModifiedVoidDarkness(LivingEntity entity, MobEffectInstance effectInstance, float f, float partialTick) { return 1.0F - effectInstance.getBlendFactor(entity, partialTick); } } @Environment(EnvType.CLIENT) static class FogData { public final FogRenderer.FogMode mode; public float start; public float end; public FogShape shape = FogShape.SPHERE; public FogData(FogRenderer.FogMode mode) { this.mode = mode; } } @Environment(EnvType.CLIENT) public static enum FogMode { FOG_SKY, FOG_TERRAIN; } @Environment(EnvType.CLIENT) interface MobEffectFogFunction { Holder getMobEffect(); void setupFog(FogRenderer.FogData fogData, LivingEntity entity, MobEffectInstance effectInstance, float farPlaneDistance, float f); default boolean isEnabled(LivingEntity entity, float f) { return entity.hasEffect(this.getMobEffect()); } default float getModifiedVoidDarkness(LivingEntity entity, MobEffectInstance effectInstance, float f, float partialTick) { MobEffectInstance mobEffectInstance = entity.getEffect(this.getMobEffect()); if (mobEffectInstance != null) { if (mobEffectInstance.endsWithin(19)) { f = 1.0F - mobEffectInstance.getDuration() / 20.0F; } else { f = 0.0F; } } return f; } } }