354 lines
12 KiB
Java
354 lines
12 KiB
Java
package net.minecraft.client.renderer;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.mojang.blaze3d.shaders.FogShape;
|
|
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.ARGB;
|
|
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;
|
|
import org.joml.Vector4f;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public class FogRenderer {
|
|
private static final int WATER_FOG_DISTANCE = 96;
|
|
private static final List<FogRenderer.MobEffectFogFunction> MOB_EFFECT_FOG = Lists.<FogRenderer.MobEffectFogFunction>newArrayList(
|
|
new FogRenderer.BlindnessFogFunction(), new FogRenderer.DarknessFogFunction()
|
|
);
|
|
public static final float BIOME_FOG_TRANSITION_TIME = 5000.0F;
|
|
private static int targetBiomeFog = -1;
|
|
private static int previousBiomeFog = -1;
|
|
private static long biomeChangedTime = -1L;
|
|
private static boolean fogEnabled = true;
|
|
|
|
public static Vector4f computeFogColor(Camera camera, float partialTick, ClientLevel level, int renderDistance, float darkenWorldAmount) {
|
|
FogType fogType = camera.getFluidInCamera();
|
|
Entity entity = camera.getEntity();
|
|
float r;
|
|
float s;
|
|
float t;
|
|
if (fogType == FogType.WATER) {
|
|
long l = Util.getMillis();
|
|
int i = level.getBiome(BlockPos.containing(camera.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);
|
|
r = g / 255.0F;
|
|
s = h / 255.0F;
|
|
t = 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) {
|
|
r = 0.6F;
|
|
s = 0.1F;
|
|
t = 0.0F;
|
|
biomeChangedTime = -1L;
|
|
} else if (fogType == FogType.POWDER_SNOW) {
|
|
r = 0.623F;
|
|
s = 0.734F;
|
|
t = 0.785F;
|
|
biomeChangedTime = -1L;
|
|
} else {
|
|
float u = 0.25F + 0.75F * renderDistance / 32.0F;
|
|
u = 1.0F - (float)Math.pow(u, 0.25);
|
|
int v = level.getSkyColor(camera.getPosition(), partialTick);
|
|
float w = ARGB.redFloat(v);
|
|
float x = ARGB.greenFloat(v);
|
|
float y = ARGB.blueFloat(v);
|
|
float z = Mth.clamp(Mth.cos(level.getTimeOfDay(partialTick) * (float) (Math.PI * 2)) * 2.0F + 0.5F, 0.0F, 1.0F);
|
|
BiomeManager biomeManager = level.getBiomeManager();
|
|
Vec3 vec3 = camera.getPosition().subtract(2.0, 2.0, 2.0).scale(0.25);
|
|
Vec3 vec32 = CubicSampler.gaussianSampleVec3(
|
|
vec3, (ix, j, k) -> level.effects().getBrightnessDependentFogColor(Vec3.fromRGB24(biomeManager.getNoiseBiomeAtQuart(ix, j, k).value().getFogColor()), z)
|
|
);
|
|
r = (float)vec32.x();
|
|
s = (float)vec32.y();
|
|
t = (float)vec32.z();
|
|
if (renderDistance >= 4) {
|
|
float f = Mth.sin(level.getSunAngle(partialTick)) > 0.0F ? -1.0F : 1.0F;
|
|
Vector3f vector3f = new Vector3f(f, 0.0F, 0.0F);
|
|
float h = camera.getLookVector().dot(vector3f);
|
|
if (h < 0.0F) {
|
|
h = 0.0F;
|
|
}
|
|
|
|
if (h > 0.0F && level.effects().isSunriseOrSunset(level.getTimeOfDay(partialTick))) {
|
|
int aa = level.effects().getSunriseOrSunsetColor(level.getTimeOfDay(partialTick));
|
|
h *= ARGB.alphaFloat(aa);
|
|
r = r * (1.0F - h) + ARGB.redFloat(aa) * h;
|
|
s = s * (1.0F - h) + ARGB.greenFloat(aa) * h;
|
|
t = t * (1.0F - h) + ARGB.blueFloat(aa) * h;
|
|
}
|
|
}
|
|
|
|
r += (w - r) * u;
|
|
s += (x - s) * u;
|
|
t += (y - t) * u;
|
|
float fx = level.getRainLevel(partialTick);
|
|
if (fx > 0.0F) {
|
|
float g = 1.0F - fx * 0.5F;
|
|
float hx = 1.0F - fx * 0.4F;
|
|
r *= g;
|
|
s *= g;
|
|
t *= hx;
|
|
}
|
|
|
|
float g = level.getThunderLevel(partialTick);
|
|
if (g > 0.0F) {
|
|
float hx = 1.0F - g * 0.5F;
|
|
r *= hx;
|
|
s *= hx;
|
|
t *= hx;
|
|
}
|
|
|
|
biomeChangedTime = -1L;
|
|
}
|
|
|
|
float ux = ((float)camera.getPosition().y - level.getMinY()) * level.getLevelData().getClearColorScale();
|
|
FogRenderer.MobEffectFogFunction mobEffectFogFunction = getPriorityFogFunction(entity, partialTick);
|
|
if (mobEffectFogFunction != null) {
|
|
LivingEntity livingEntity = (LivingEntity)entity;
|
|
ux = mobEffectFogFunction.getModifiedVoidDarkness(livingEntity, livingEntity.getEffect(mobEffectFogFunction.getMobEffect()), ux, partialTick);
|
|
}
|
|
|
|
if (ux < 1.0F && fogType != FogType.LAVA && fogType != FogType.POWDER_SNOW) {
|
|
if (ux < 0.0F) {
|
|
ux = 0.0F;
|
|
}
|
|
|
|
ux *= ux;
|
|
r *= ux;
|
|
s *= ux;
|
|
t *= ux;
|
|
}
|
|
|
|
if (darkenWorldAmount > 0.0F) {
|
|
r = r * (1.0F - darkenWorldAmount) + r * 0.7F * darkenWorldAmount;
|
|
s = s * (1.0F - darkenWorldAmount) + s * 0.6F * darkenWorldAmount;
|
|
t = t * (1.0F - darkenWorldAmount) + t * 0.6F * darkenWorldAmount;
|
|
}
|
|
|
|
float wx;
|
|
if (fogType == FogType.WATER) {
|
|
if (entity instanceof LocalPlayer) {
|
|
wx = ((LocalPlayer)entity).getWaterVision();
|
|
} else {
|
|
wx = 1.0F;
|
|
}
|
|
} else if (entity instanceof LivingEntity livingEntity2 && livingEntity2.hasEffect(MobEffects.NIGHT_VISION) && !livingEntity2.hasEffect(MobEffects.DARKNESS)) {
|
|
wx = GameRenderer.getNightVisionScale(livingEntity2, partialTick);
|
|
} else {
|
|
wx = 0.0F;
|
|
}
|
|
|
|
if (r != 0.0F && s != 0.0F && t != 0.0F) {
|
|
float xx = Math.min(1.0F / r, Math.min(1.0F / s, 1.0F / t));
|
|
r = r * (1.0F - wx) + r * xx * wx;
|
|
s = s * (1.0F - wx) + s * xx * wx;
|
|
t = t * (1.0F - wx) + t * xx * wx;
|
|
}
|
|
|
|
return new Vector4f(r, s, t, 1.0F);
|
|
}
|
|
|
|
public static boolean toggleFog() {
|
|
return fogEnabled = !fogEnabled;
|
|
}
|
|
|
|
@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 FogParameters setupFog(Camera camera, FogRenderer.FogMode fogMode, Vector4f fogColor, float renderDistance, boolean isFoggy, float partialTick) {
|
|
if (!fogEnabled) {
|
|
return FogParameters.NO_FOG;
|
|
} else {
|
|
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 = renderDistance * 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 = renderDistance * 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, renderDistance, 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<Biome> holder = localPlayer.level().getBiome(localPlayer.blockPosition());
|
|
if (holder.is(BiomeTags.HAS_CLOSER_WATER_FOG)) {
|
|
fogData.end *= 0.85F;
|
|
}
|
|
}
|
|
|
|
if (fogData.end > renderDistance) {
|
|
fogData.end = renderDistance;
|
|
fogData.shape = FogShape.CYLINDER;
|
|
}
|
|
} else if (isFoggy) {
|
|
fogData.start = renderDistance * 0.05F;
|
|
fogData.end = Math.min(renderDistance, 192.0F) * 0.5F;
|
|
} else if (fogMode == FogRenderer.FogMode.FOG_SKY) {
|
|
fogData.start = 0.0F;
|
|
fogData.end = renderDistance;
|
|
fogData.shape = FogShape.CYLINDER;
|
|
} else if (fogMode == FogRenderer.FogMode.FOG_TERRAIN) {
|
|
float f = Mth.clamp(renderDistance / 10.0F, 4.0F, 64.0F);
|
|
fogData.start = renderDistance - f;
|
|
fogData.end = renderDistance;
|
|
fogData.shape = FogShape.CYLINDER;
|
|
}
|
|
|
|
return new FogParameters(fogData.start, fogData.end, fogData.shape, fogColor.x, fogColor.y, fogColor.z, fogColor.w);
|
|
}
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
static class BlindnessFogFunction implements FogRenderer.MobEffectFogFunction {
|
|
@Override
|
|
public Holder<MobEffect> getMobEffect() {
|
|
return MobEffects.BLINDNESS;
|
|
}
|
|
|
|
@Override
|
|
public void setupFog(FogRenderer.FogData fogData, LivingEntity entity, MobEffectInstance effectInstance, float farPlaneDistance, float partialTick) {
|
|
float f = 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 = f * 0.8F;
|
|
} else if (fogData.mode == FogRenderer.FogMode.FOG_TERRAIN) {
|
|
fogData.start = f * 0.25F;
|
|
fogData.end = f;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
static class DarknessFogFunction implements FogRenderer.MobEffectFogFunction {
|
|
@Override
|
|
public Holder<MobEffect> getMobEffect() {
|
|
return MobEffects.DARKNESS;
|
|
}
|
|
|
|
@Override
|
|
public void setupFog(FogRenderer.FogData fogData, LivingEntity entity, MobEffectInstance effectInstance, float farPlaneDistance, float partialTick) {
|
|
float f = Mth.lerp(effectInstance.getBlendFactor(entity, partialTick), farPlaneDistance, 15.0F);
|
|
|
|
fogData.start = switch (fogData.mode) {
|
|
case FOG_SKY -> 0.0F;
|
|
case FOG_TERRAIN -> f * 0.75F;
|
|
};
|
|
fogData.end = f;
|
|
}
|
|
|
|
@Override
|
|
public float getModifiedVoidDarkness(LivingEntity entity, MobEffectInstance effectInstance, float voidDarkness, 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<MobEffect> getMobEffect();
|
|
|
|
void setupFog(FogRenderer.FogData fogData, LivingEntity entity, MobEffectInstance effectInstance, float farPlaneDistance, float partialTick);
|
|
|
|
default boolean isEnabled(LivingEntity entity, float partialTick) {
|
|
return entity.hasEffect(this.getMobEffect());
|
|
}
|
|
|
|
default float getModifiedVoidDarkness(LivingEntity entity, MobEffectInstance effectInstance, float voidDarkness, float partialTick) {
|
|
MobEffectInstance mobEffectInstance = entity.getEffect(this.getMobEffect());
|
|
if (mobEffectInstance != null) {
|
|
if (mobEffectInstance.endsWithin(19)) {
|
|
voidDarkness = 1.0F - mobEffectInstance.getDuration() / 20.0F;
|
|
} else {
|
|
voidDarkness = 0.0F;
|
|
}
|
|
}
|
|
|
|
return voidDarkness;
|
|
}
|
|
}
|
|
}
|