Skip to content

Commit

Permalink
feat: Sunset/sunrise fog implementation. (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
IMB11 authored Dec 27, 2024
1 parent 4e1e266 commit 09ac55d
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 34 deletions.
96 changes: 64 additions & 32 deletions src/main/java/dev/imb11/fog/client/FogManager.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dev.imb11.fog.client;

import dev.imb11.fog.api.CustomFogDefinition;
import dev.imb11.fog.api.FogColors;
import dev.imb11.fog.client.registry.FogRegistry;
import dev.imb11.fog.api.CustomFogDefinition;
import dev.imb11.fog.client.util.color.Color;
import dev.imb11.fog.client.util.math.DarknessCalculation;
import dev.imb11.fog.client.util.math.InterpolatedValue;
Expand All @@ -12,8 +12,10 @@
import dev.imb11.fog.config.FogConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.CubicSampler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.LightType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -62,6 +64,28 @@ public FogManager() {
return INSTANCE;
}

private static float getBlendFactor(@NotNull ClientWorld world) {
long time = world.getTimeOfDay() % 24000;
float blendFactor;
if (time < 11000) {
// Daytime
blendFactor = 1.0f;
} else if (time < 13000) {
// Blend from day to night
blendFactor = MathUtil.lerp(1.0f, 0.0f, (time - 11000) / 2000f);
} else if (time < 22000) {
// Nighttime
blendFactor = 0.0f;
} else if (time < 23000) {
// Blend from night to day
blendFactor = MathUtil.lerp(0.0f, 1.0f, (time - 22000) / 1000f);
} else {
// Constant day from 23000 to 24000 ticks
blendFactor = 1.0f;
}
return blendFactor;
}

public void onEndTick(@NotNull ClientWorld clientWorld) {
@NotNull final var client = MinecraftClient.getInstance();
@Nullable final var clientPlayer = client.player;
Expand Down Expand Up @@ -89,13 +113,8 @@ public void onEndTick(@NotNull ClientWorld clientWorld) {
}

float density = ClientWorldUtil.isFogDenseAtPosition(clientWorld, clientPlayerBlockPosition) ? 0.9F : 1.0F;
/*? if <1.21 {*/
/*float tickDelta = client.getTickDelta();
*//*?} else {*/
float tickDelta = client.getRenderTickCounter().getTickDelta(true);

/*?}*/
// TODO: Apply the start and end multipliers in FogManager#getFogSettings
DarknessCalculation darknessCalculation = DarknessCalculation.of(
client, fogStart.getDefaultValue(), fogEnd.getDefaultValue() * density, tickDelta);
@NotNull var clientPlayerBiomeKeyOptional = clientWorld.getBiome(clientPlayer.getBlockPos()).getKey();
Expand All @@ -112,6 +131,8 @@ public void onEndTick(@NotNull ClientWorld clientWorld) {

float blendFactor = getBlendFactor(clientWorld);
Color finalNightColor = getFinalNightColor(clientWorld, colors);

// Base day/night color blending
float red = MathHelper.lerp(blendFactor, finalNightColor.red / 255f, colors.getDayColor().red / 255f);
float green = MathHelper.lerp(blendFactor, finalNightColor.green / 255f, colors.getDayColor().green / 255f);
float blue = MathHelper.lerp(blendFactor, finalNightColor.blue / 255f, colors.getDayColor().blue / 255f);
Expand All @@ -120,7 +141,6 @@ public void onEndTick(@NotNull ClientWorld clientWorld) {
this.fogColorRed.set(red);
this.fogColorGreen.set(green);
this.fogColorBlue.set(blue);

hasSetup = true;
} else {
this.fogColorRed.interpolate(red);
Expand All @@ -140,37 +160,15 @@ public void onEndTick(@NotNull ClientWorld clientWorld) {
this.currentLight.interpolate(clientWorld.getBaseLightLevel(clientPlayerBlockPosition, 0));
}

private static float getBlendFactor(@NotNull ClientWorld world) {
long time = world.getTimeOfDay() % 24000;
float blendFactor;
if (time < 11000) {
// Daytime
blendFactor = 1.0f;
} else if (time < 13000) {
// Blend from day to night
blendFactor = MathUtil.lerp(1.0f, 0.0f, (time - 11000) / 2000f);
} else if (time < 22000) {
// Nighttime
blendFactor = 0.0f;
} else if (time < 23000) {
// Blend from night to day
blendFactor = MathUtil.lerp(0.0f, 1.0f, (time - 22000) / 1000f);
} else {
// Constant day from 23000 to 24000 ticks
blendFactor = 1.0f;
}
return blendFactor;
}

private Color getFinalNightColor(@NotNull ClientWorld world, FogColors fogColors) {
Color newMoonColor = Color.from(FogConfig.getInstance().newMoonColor);
if (!FogConfig.getInstance().disableMoonPhaseColorTransition) {
float blendFactor = switch (world.getMoonPhase()) {
case 0 -> 0.0f; // new moon
case 0 -> 0.0f; // new moon
case 1, 7 -> 0.25f; // 1/4 moon
case 2, 6 -> 0.5f; // 1/2 moon
case 3, 5 -> 0.75f; // 3/4 moon
case 4 -> 1.0f; // full moon
case 4 -> 1.0f; // full moon
default -> 1.0f;
};
return fogColors.getNightColor().lerp(newMoonColor, blendFactor);
Expand Down Expand Up @@ -198,6 +196,8 @@ public float getUndergroundFactor(@NotNull MinecraftClient client, float deltaTi
}

public @NotNull FogSettings getFogSettings(float tickDelta, float viewDistance) {
MinecraftClient client = MinecraftClient.getInstance();

float fogStartValue = fogStart.get(tickDelta) * viewDistance;
// Default to no multiplier
float undergroundFogMultiplier = 1.0F;
Expand All @@ -217,7 +217,6 @@ public float getUndergroundFactor(@NotNull MinecraftClient client, float deltaTi
float fogBlue = fogColorBlue.get(tickDelta);

float raininessValue = raininess.get(tickDelta);

if (!FogConfig.getInstance().disableRaininessEffect && raininessValue > 0.0f) {
fogEndValue /= 1.0f + raininessValue;

Expand All @@ -235,8 +234,41 @@ public float getUndergroundFactor(@NotNull MinecraftClient client, float deltaTi
fogStartValue *= this.currentStartMultiplier.get(tickDelta);
fogEndValue *= this.currentEndMultiplier.get(tickDelta);

// Sunset
float[] sunsetAdjustedColors = applySunsetLogic(client, fogRed, fogGreen, fogBlue, tickDelta);
fogRed = sunsetAdjustedColors[0];
fogGreen = sunsetAdjustedColors[1];
fogBlue = sunsetAdjustedColors[2];

return new FogSettings(fogStartValue, fogEndValue, fogRed, fogGreen, fogBlue);
}

public float sunsetSunriseBlendFactor = 0.0F;

/**
* Applies sunset color blending similar to vanilla Minecraft's implementation.
*/
private float[] applySunsetLogic(MinecraftClient client, float red, float green, float blue, float tickDelta) {
if (!FogConfig.getInstance().disableSunsetFog && client.world != null) {
float skyAngle = client.world.getSkyAngle(tickDelta);
float blendSpeed = 0.0005F; // Speed of blending

Vec3d sunColor = Vec3d.unpackRgb(client.world.getDimensionEffects().getSkyColor(skyAngle));

if (client.world.getDimensionEffects().isSunRisingOrSetting(skyAngle)) {
sunsetSunriseBlendFactor = Math.min(sunsetSunriseBlendFactor + (blendSpeed * tickDelta), 1.0F);
} else {
sunsetSunriseBlendFactor = Math.max(sunsetSunriseBlendFactor - (blendSpeed * tickDelta), 0.0F);
}

// Interpolate each color component towards the sunset color
red = MathHelper.lerp(sunsetSunriseBlendFactor, red, (float) sunColor.x);
green = MathHelper.lerp(sunsetSunriseBlendFactor, green, (float) sunColor.y);
blue = MathHelper.lerp(sunsetSunriseBlendFactor, blue, (float) sunColor.z);
}

return new float[]{red, green, blue};
}

public record FogSettings(double fogStart, double fogEnd, float fogRed, float fogGreen, float fogBlue) {}
}
5 changes: 5 additions & 0 deletions src/main/java/dev/imb11/fog/client/util/color/Color.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.imb11.fog.client.util.math.MathUtil;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
Expand Down Expand Up @@ -43,6 +44,10 @@ public static Color from(java.awt.Color awtColor) {
return colorCache.computeIfAbsent(awtColor, color -> new Color(color.getRed(), color.getGreen(), color.getBlue()));
}

public Vec3d asVec3d() {
return new Vec3d(red / 255.0f, green / 255.0f, blue / 255.0f);
}

@Override
public String toString() {
return String.format("Color(%s, %s, %s)", red, green, blue);
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/dev/imb11/fog/config/FogConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class FogConfig {
@SerialEntry
public boolean disableBiomeFogColour = false;
@SerialEntry
public boolean disableCloudWhitening = false;
public boolean disableCloudWhitening = true;
@SerialEntry
public float initialFogStart = 0.1f;
@SerialEntry
Expand All @@ -75,6 +75,8 @@ public class FogConfig {
public boolean disableMoonPhaseColorTransition = false;
@SerialEntry
public Color newMoonColor = new Color(0, 0, 0, 255);
@SerialEntry
public boolean disableSunsetFog = false;

public static @NotNull FogConfig getInstance() {
return HANDLER.instance();
Expand Down Expand Up @@ -159,6 +161,10 @@ public static void save() {
.option(HELPER.get(
"new_moon_color", defaults.newMoonColor, () -> config.newMoonColor, val -> config.newMoonColor = val
))
.option(HELPER.get(
"disable_sunset_fog", defaults.disableSunsetFog,
() -> config.disableSunsetFog, val -> config.disableSunsetFog = val
))
.option(Option.<Boolean>createBuilder().name(
HELPER.getText(EntryType.OPTION_NAME, "disable_cloud_whitening")).description(
initialFogStart -> OptionDescription.createBuilder().text(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dev.imb11.fog.mixin.client.rendering;

import com.mojang.blaze3d.systems.RenderSystem;
import dev.imb11.fog.config.FogConfig;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.CloudRenderMode;
import net.minecraft.client.render.DimensionEffects;
import net.minecraft.client.render.Fog;
import net.minecraft.util.math.Vec3d;
import org.jetbrains.annotations.NotNull;
Expand All @@ -22,7 +24,10 @@ public class CloudRendererMixin {
private void fog$whiteClouds(int color, CloudRenderMode cloudRenderMode, float cloudHeight, Matrix4f positionMatrix, Matrix4f projectionMatrix, Vec3d cameraPos, float ticks, CallbackInfo ci) {
@NotNull var client = MinecraftClient.getInstance();
@Nullable var camera = client.gameRenderer.getCamera();
if (camera == null) {
if (camera == null || client.world == null || FogConfig.getInstance().disableMod
|| !(client.world.getDimensionEffects() instanceof DimensionEffects.Overworld)
|| FogConfig.getInstance().disableCloudWhitening
|| client.world.getDimension().hasFixedTime()) {
return;
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/assets/fog/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"fog.config.option.new_moon_color": "New Moon Color",
"fog.config.option.description.new_moon_color": "The fog color to use when the moon is in the \"New Moon\" phase. This will be lerp'd with the biome or default night color if the moon phase color transition is enabled.",
"fog.config.option.fog_calculations.warning": "§6§l§nWarning!§r It is not recommended to touch these constants unless you understand what they do, and how they are used.\nConsider leaving them as-is for the best experience.",
"fog.config.option.disable_sunset_fog": "Disable Sunset Fog",
"fog.config.option.description.disable_sunset_fog": "Disables the sunset fog effect, which makes the fog appear more orange during sunset",
"fog.config.option.sunset_color": "Sunset Color",
"fog.config.option.description.sunset_color": "The fog color to use during sunset. This will be lerp'd with the biome or default day color if the sunset fog effect is enabled.",
"fog.config.option.initial_fog_start": "Initial Fog Start",
"fog.config.option.description.initial_fog_start": "The initial (when the fog engine is loaded) distance from the camera at which fog starts to appear - expressed as a percentage of view distance",
"fog.config.option.initial_fog_end": "Initial Fog End",
Expand Down

0 comments on commit 09ac55d

Please sign in to comment.