Add the option for 3 layer clouds

This commit is contained in:
James Seibel
2026-05-30 16:38:55 -05:00
parent db47a9e99f
commit e8f27f7da8
3 changed files with 117 additions and 29 deletions
@@ -373,6 +373,14 @@ public class Config
+ "") + "")
.build(); .build();
public static ConfigEntry<Boolean> enableMultiLayerClouds = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "False = DH will render a single layer of clouds, like vanilla Minecraft. \n"
+ "True = DH will render 3 layers of clouds at different heights. \n"
+ "")
.build();
public static ConfigEntry<String> dimensionEnabledCloudRenderingCsv = new ConfigEntry.Builder<String>() public static ConfigEntry<String> dimensionEnabledCloudRenderingCsv = new ConfigEntry.Builder<String>()
.set("minecraft:overworld") .set("minecraft:overworld")
.setAppearance(EConfigEntryAppearance.ALL) .setAppearance(EConfigEntryAppearance.ALL)
@@ -74,13 +74,16 @@ public class CloudRenderHandler
*/ */
private static final int CLOUD_INSTANCE_RADIUS_COUNT = 5; private static final int CLOUD_INSTANCE_RADIUS_COUNT = 5;
/** if multi-layer clouds are enabled how many layers should be rendered? */
private static final int CLOUD_LAYER_COUNT = 3;
private static final float MOVE_SPEED_IN_BLOCKS_PER_SECOND = 6.0f; private static final float MOVE_SPEED_IN_BLOCKS_PER_SECOND = 6.0f;
private final IDhApiRenderableBoxGroup[][] boxGroupByOffset private final IDhApiRenderableBoxGroup[][][] boxGroupByOffset
// radius * 2 to get the diameter // radius * 2 to get the diameter
// + 1 so we get an odd number wide (needed so we can have a center position) // + 1 so we get an odd number wide (needed so we can have a center position)
= new IDhApiRenderableBoxGroup[(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1][(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1]; = new IDhApiRenderableBoxGroup[CLOUD_LAYER_COUNT][(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1][(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1];
private final IDhClientLevel level; private final IDhClientLevel level;
private final IDhGenericRenderer renderer; private final IDhGenericRenderer renderer;
@@ -260,12 +263,19 @@ public class CloudRenderHandler
// slightly lighter shading than the default // slightly lighter shading than the default
DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded(); DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded();
cloudShading.north = cloudShading.south = 0.9f; {
cloudShading.east = cloudShading.west = 0.8f; cloudShading.north = 0.9f;
cloudShading.south = cloudShading.north;
cloudShading.east = 0.8f;
cloudShading.west = cloudShading.east;
cloudShading.top = 1.0f; cloudShading.top = 1.0f;
cloudShading.bottom = 0.7f; cloudShading.bottom = 0.7f;
}
for (int y = CLOUD_LAYER_COUNT-1; y >= 0; y--) // start from the top down so transparency could be attempted
{
for (int x = -CLOUD_INSTANCE_RADIUS_COUNT; x <= CLOUD_INSTANCE_RADIUS_COUNT; x++) for (int x = -CLOUD_INSTANCE_RADIUS_COUNT; x <= CLOUD_INSTANCE_RADIUS_COUNT; x++)
{ {
for (int z = -CLOUD_INSTANCE_RADIUS_COUNT; z <= CLOUD_INSTANCE_RADIUS_COUNT; z++) for (int z = -CLOUD_INSTANCE_RADIUS_COUNT; z <= CLOUD_INSTANCE_RADIUS_COUNT; z++)
@@ -281,11 +291,14 @@ public class CloudRenderHandler
boxGroup.setSsaoEnabled(false); boxGroup.setSsaoEnabled(false);
boxGroup.setShading(cloudShading); boxGroup.setShading(cloudShading);
CloudParams cloudParams = new CloudParams(textureWidth, x, z); CloudParams cloudParams = new CloudParams(
textureWidth,
y, x, z);
boxGroup.setPreRenderFunc((renderParam) -> this.preRender(renderParam, cloudParams)); boxGroup.setPreRenderFunc((renderParam) -> this.preRender(renderParam, cloudParams));
this.renderer.add(boxGroup); this.renderer.add(boxGroup);
this.boxGroupByOffset[x+CLOUD_INSTANCE_RADIUS_COUNT][z+CLOUD_INSTANCE_RADIUS_COUNT] = boxGroup; this.boxGroupByOffset[y][x + CLOUD_INSTANCE_RADIUS_COUNT][z + CLOUD_INSTANCE_RADIUS_COUNT] = boxGroup;
}
} }
} }
} }
@@ -301,15 +314,24 @@ public class CloudRenderHandler
private void preRender(DhApiRenderParam renderParam, CloudParams cloudParams) private void preRender(DhApiRenderParam renderParam, CloudParams cloudParams)
{ {
IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[cloudParams.instanceOffsetX+CLOUD_INSTANCE_RADIUS_COUNT][cloudParams.instanceOffsetZ+CLOUD_INSTANCE_RADIUS_COUNT]; IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[cloudParams.instanceOffsetY][cloudParams.instanceOffsetX+CLOUD_INSTANCE_RADIUS_COUNT][cloudParams.instanceOffsetZ+CLOUD_INSTANCE_RADIUS_COUNT];
//===================// //===================//
// should we render? // // should we render? //
//===================// //===================//
//region
boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get(); boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get();
boolean renderSingleCloudLayer = !Config.Client.Advanced.Graphics.GenericRendering.enableMultiLayerClouds.get();
if (renderSingleCloudLayer
&& cloudParams.instanceOffsetY != 0)
{
renderClouds = false;
}
boxGroup.setActive(renderClouds); boxGroup.setActive(renderClouds);
if(!renderClouds) if(!renderClouds)
{ {
@@ -322,27 +344,33 @@ public class CloudRenderHandler
return; return;
} }
//endregion
//================// //================//
// cloud movement // // cloud movement //
//================// //================//
//region
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
float deltaTime = (currentTime - cloudParams.lastFrameTime) / 1000.0f; // Delta time in seconds float deltaTime = (currentTime - cloudParams.lastFrameTime) / 1000.0f; // Delta time in seconds
cloudParams.lastFrameTime = currentTime; cloudParams.lastFrameTime = currentTime;
float deltaX = MOVE_SPEED_IN_BLOCKS_PER_SECOND * deltaTime; float deltaX = (MOVE_SPEED_IN_BLOCKS_PER_SECOND + cloudParams.heightSpeedOffset) * deltaTime;
// negative delta is to match vanilla's cloud movement // negative delta is to match vanilla's cloud movement
cloudParams.deltaOffsetX -= deltaX; cloudParams.deltaOffsetX -= deltaX;
// wrap the cloud around after reaching the edge // wrap the cloud around after reaching the edge
cloudParams.deltaOffsetX %= cloudParams.widthInBlocks; cloudParams.deltaOffsetX %= cloudParams.widthInBlocks;
//endregion
//============================// //============================//
// camera movement and offset // // camera movement and offset //
//============================// //============================//
//region
// camera position // camera position
int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x; int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x;
@@ -363,7 +391,10 @@ public class CloudRenderHandler
cloudParams.deltaOffsetX cloudParams.deltaOffsetX
+ (cloudParams.instanceOffsetX * cloudParams.widthInBlocks) + (cloudParams.instanceOffsetX * cloudParams.widthInBlocks)
+ instanceOffsetX + cloudParams.halfWidthInBlocks; + instanceOffsetX + cloudParams.halfWidthInBlocks;
float newMinPosY = this.level.getLevelWrapper().getMaxHeight() + 200; float newMinPosY =
this.level.getLevelWrapper().getMaxHeight()
+ 200 // render clouds at least 200 blocks above the height limit to prevent players/blocks from intersecting (since DH always renders behind everything else)
+ cloudParams.heightOffset;
float newMinPosZ = cloudParams.deltaOffsetZ float newMinPosZ = cloudParams.deltaOffsetZ
+ (cloudParams.instanceOffsetZ * cloudParams.widthInBlocks) + (cloudParams.instanceOffsetZ * cloudParams.widthInBlocks)
+ instanceOffsetZ + cloudParams.halfWidthInBlocks; + instanceOffsetZ + cloudParams.halfWidthInBlocks;
@@ -377,11 +408,16 @@ public class CloudRenderHandler
boxGroup.setActive(false); boxGroup.setActive(false);
} }
boxGroup.setOriginBlockPos(new DhApiVec3d(newMinPosX, newMinPosY, newMinPosZ));
//endregion
//===========================// //===========================//
// update color and position // // update color and position //
//===========================// //===========================//
//region
// if debug colors are enabled don't change them // if debug colors are enabled don't change them
if (!DEBUG_BORDER_COLORS if (!DEBUG_BORDER_COLORS
@@ -390,6 +426,27 @@ public class CloudRenderHandler
{ {
// cloud color changes based on the time of day and weather so we need to get it from the level // cloud color changes based on the time of day and weather so we need to get it from the level
Color newCloudColor = clientLevelWrapper.getCloudColor(renderParam.partialTicks); Color newCloudColor = clientLevelWrapper.getCloudColor(renderParam.partialTicks);
Color sky = MC_RENDER.getSkyColor();
float skyColPercent = 0.0f;
switch (cloudParams.instanceOffsetY)
{
case 0:
skyColPercent = 0.0f; // use only cloud color
break;
case 1:
skyColPercent = 0.15f; // use some sky color
break;
case 2:
skyColPercent = 0.30f;
break;
}
newCloudColor = new Color(
(int) mixColors(newCloudColor.getRed(), sky.getRed(), skyColPercent),
(int) mixColors(newCloudColor.getGreen(), sky.getGreen(), skyColPercent),
(int) mixColors(newCloudColor.getBlue(), sky.getBlue(), skyColPercent)
);
// all boxes should have the same color, so we can get their current color // all boxes should have the same color, so we can get their current color
@@ -418,8 +475,13 @@ public class CloudRenderHandler
} }
} }
boxGroup.setOriginBlockPos(new DhApiVec3d(newMinPosX, newMinPosY, newMinPosZ)); //endregion
} }
/**
* based on the OpenGL spec <br>
* https://registry.khronos.org/OpenGL-Refpages/gl4/html/mix.xhtml
*/
private float mixColors(float x, float y, float a) { return x * (1 - a) + y * a; }
private boolean shouldCloudBeCulled( private boolean shouldCloudBeCulled(
float minPosX, float minPosY, float minPosZ, float minPosX, float minPosY, float minPosZ,
CloudParams cloudParams) CloudParams cloudParams)
@@ -568,9 +630,13 @@ public class CloudRenderHandler
public final int widthInBlocks; public final int widthInBlocks;
public final int halfWidthInBlocks; public final int halfWidthInBlocks;
public final int instanceOffsetY;
public final int instanceOffsetX; public final int instanceOffsetX;
public final int instanceOffsetZ; public final int instanceOffsetZ;
public final int heightOffset;
public final float heightSpeedOffset;
/** how far this cloud group has moved in the X direction based on time */ /** how far this cloud group has moved in the X direction based on time */
public float deltaOffsetX = 0; public float deltaOffsetX = 0;
@@ -586,14 +652,25 @@ public class CloudRenderHandler
// constructor // // constructor //
public CloudParams(int textureWidth, int instanceOffsetX, int instanceOffsetZ) public CloudParams(int textureWidth, int instanceOffsetY, int instanceOffsetX, int instanceOffsetZ)
{ {
this.textureWidth = textureWidth; this.textureWidth = textureWidth;
this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH); this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH);
this.halfWidthInBlocks = this.widthInBlocks / 2; this.halfWidthInBlocks = this.widthInBlocks / 2;
this.instanceOffsetY = instanceOffsetY;
this.instanceOffsetX = instanceOffsetX; this.instanceOffsetX = instanceOffsetX;
this.instanceOffsetZ = instanceOffsetZ; this.instanceOffsetZ = instanceOffsetZ;
// each layer up increases by 512 blocks
this.heightOffset = instanceOffsetY * 512;
// have higher cloud layers move faster
this.heightSpeedOffset = instanceOffsetY * 10f;
// offset each layer a bit so the duplicated texture use isn't as obvious
this.deltaOffsetX = this.widthInBlocks * instanceOffsetY * 0.75f;
this.deltaOffsetZ = this.widthInBlocks * instanceOffsetY * 1.5f;
} }
} }
@@ -201,7 +201,10 @@
"Cloud Enabled Dimension CSV List", "Cloud Enabled Dimension CSV List",
"distanthorizons.config.client.advanced.graphics.genericRendering.dimensionEnabledCloudRenderingCsv.@tooltip": "distanthorizons.config.client.advanced.graphics.genericRendering.dimensionEnabledCloudRenderingCsv.@tooltip":
"A comma separated separated list of dimension resource locations where DH clouds will render. \n\nExample: \"minecraft:overworld,minecraft:the_end\" \n\nChanges require a world re-load.", "A comma separated separated list of dimension resource locations where DH clouds will render. \n\nExample: \"minecraft:overworld,minecraft:the_end\" \n\nChanges require a world re-load.",
"distanthorizons.config.client.advanced.graphics.genericRendering.enableMultiLayerClouds":
"Enable Multi-layer clouds",
"distanthorizons.config.client.advanced.graphics.genericRendering.enableMultiLayerClouds.@tooltip":
"False = DH will render a single layer of clouds, like vanilla Minecraft. \nTrue = DH will render 3 layers of clouds at different heights.",
"distanthorizons.config.client.advanced.graphics.fog": "distanthorizons.config.client.advanced.graphics.fog":