Add unexplored ocean for overworld

This commit is contained in:
James Seibel
2025-09-10 07:46:21 -05:00
parent 9ffda4d43e
commit 1ec536b7df
7 changed files with 156 additions and 42 deletions
@@ -0,0 +1,30 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.enums;
/**
* OCEAN, <br>
* FOG_WALL, <br>
*/
public enum EUnexploredTerrainType
{
OCEAN,
FOG_WALL
}
@@ -26,6 +26,7 @@ import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EUnexploredTerrainType;
import com.seibel.distanthorizons.core.file.AbstractDataSourceHandler;
import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker;
import com.seibel.distanthorizons.core.generation.tasks.InProgressWorldGenTaskGroup;
@@ -660,14 +661,15 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
{
// determine the height the wireframe should render at
final int maxY;
if (Config.Client.Advanced.Graphics.GenericRendering.enableUnexploredFogRendering.get())
if (Config.Client.Advanced.Graphics.GenericRendering.enableUnexploredFogRendering.get()
&& this.level.getUnexploredTerrainType() == EUnexploredTerrainType.FOG_WALL)
{
// if unexplored fog is enabled, make sure the wireframe can be seen over it
maxY = this.level.getMaxY();
}
else
{
// if unexplored fog is disabled, show the wireframe a bit lower
// if unexplored fog is disabled or is an ocean, show the wireframe a bit lower
// since most worlds don't render all the way up to the max height
int levelHeightRange = (this.level.getMaxY() - this.level.getMinY());
maxY = this.level.getMaxY() - (levelHeightRange / 2);
@@ -19,12 +19,16 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.enums.EUnexploredTerrainType;
import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -39,14 +43,18 @@ import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO;
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.KeyedLockContainer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -80,6 +88,7 @@ public abstract class AbstractDhLevel implements IDhLevel
protected CloudRenderHandler cloudRenderHandler;
private IDhApiRenderableBoxGroup unexploredFogRenderableBoxGroup;
private EUnexploredTerrainType unexploredTerrainType = null;
@@ -369,22 +378,25 @@ public abstract class AbstractDhLevel implements IDhLevel
//================//
// unexplored fog //
//================//
//====================//
// unexplored terrain //
//====================//
// TODO potentially merge how this and getGenericRenderer() are handled
// synchronized to prevent issues with two threads getting the same un-initalized group at the same time
public synchronized IDhApiRenderableBoxGroup getUnexploredFogRenderableBoxGroup()
public synchronized IDhApiRenderableBoxGroup getUnexploredTerrainRenderableBoxGroup()
{
// lazy setup to prevent issues on server levels and
// prevent order issues with the genericRenderer
if (this.unexploredFogRenderableBoxGroup == null)
{
// ocean looks better without SSAO
boolean enableSsao = (this.getUnexploredTerrainType() == EUnexploredTerrainType.FOG_WALL);
this.unexploredFogRenderableBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":UnexploredFog", new ArrayList<>(512));
this.unexploredFogRenderableBoxGroup.setBlockLight(LodUtil.MIN_MC_LIGHT);
this.unexploredFogRenderableBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
this.unexploredFogRenderableBoxGroup.setSsaoEnabled(true);
this.unexploredFogRenderableBoxGroup.setSsaoEnabled(enableSsao);
this.unexploredFogRenderableBoxGroup.setShading(DhApiRenderableBoxGroupShading.getDefaultShaded());
this.unexploredFogRenderableBoxGroup.setPreRenderFunc((DhApiRenderParam param) ->
{
@@ -402,6 +414,95 @@ public abstract class AbstractDhLevel implements IDhLevel
return this.unexploredFogRenderableBoxGroup;
}
@Override
public EUnexploredTerrainType getUnexploredTerrainType()
{
// use cached value to prevent repeat string/levelWrapper operations
if (this.unexploredTerrainType != null)
{
return this.unexploredTerrainType;
}
// determine if we should use an infinite ocean or a fog wall
boolean hasCeiling = this.getLevelWrapper().hasCeiling();
String dimensionName = this.getLevelWrapper().getDimensionName().toLowerCase();
boolean dimensionHasOcean =
!hasCeiling
&& !dimensionName.contains("the_end")
&& !dimensionName.contains("nether");
this.unexploredTerrainType = dimensionHasOcean ? EUnexploredTerrainType.OCEAN : EUnexploredTerrainType.FOG_WALL;
return this.unexploredTerrainType;
}
/**
* @param levelWrapper is passed in due to how levelWrapper caching is poorly handled in most
* {@link IDhLevel}'s. If that's ever fixed we can just use the local {@link IClientLevelWrapper}
* getter instead.
*/
@Override
public DhApiRenderableBox createUnexploredTerrainRenderableBox(long pos, IClientLevelWrapper levelWrapper)
{
EUnexploredTerrainType terrainType = this.getUnexploredTerrainType();
if (terrainType == EUnexploredTerrainType.OCEAN)
{
return createUnexploredOceanRenderableBox(pos, levelWrapper);
}
else
{
return createUnexploredFogWallRenderableBox(pos, levelWrapper);
}
}
private static DhApiRenderableBox createUnexploredOceanRenderableBox(long pos, IClientLevelWrapper levelWrapper)
{
// width
float fogWidthInBlocks = (float) DhSectionPos.getBlockWidth(pos);
int seaLevel = levelWrapper.getSeaLevel();
Color waterColor = ColorUtil.toColorObjRGB(levelWrapper.getWaterBlockColor());
return new DhApiRenderableBox(
// min pos
new DhApiVec3d(DhSectionPos.getMinCornerBlockX(pos),
levelWrapper.getMinHeight(),
DhSectionPos.getMinCornerBlockZ(pos)),
// max pos
new DhApiVec3d(DhSectionPos.getMinCornerBlockX(pos) + fogWidthInBlocks,
seaLevel,
DhSectionPos.getMinCornerBlockZ(pos) + fogWidthInBlocks),
waterColor, EDhApiBlockMaterial.UNKNOWN);
}
private static DhApiRenderableBox createUnexploredFogWallRenderableBox(long pos, IClientLevelWrapper levelWrapper)
{
// width
float fogWidthInBlocks = (float) DhSectionPos.getBlockWidth(pos);
// pseudo random height (should be consistent for a given position)
int fogHeightRange = (int) ((levelWrapper.getMaxHeight() - levelWrapper.getMinHeight()) * 0.25);
int halfFogHeightRange = fogHeightRange / 2;
float randomHeightModifier = (float) (DhSectionPos.hashCode(pos) % halfFogHeightRange) - fogHeightRange;
// pseudo random color (should be consistent for a given position)
int randomColorModifier = (DhSectionPos.hashCode(pos) % 30) - 15;
int randomGrayColorValue = 180 + randomColorModifier;
randomGrayColorValue = MathUtil.clamp(1, randomGrayColorValue, 256); // clamp to prevent accidental out-of-range colors
return new DhApiRenderableBox(
// min pos
new DhApiVec3d(DhSectionPos.getMinCornerBlockX(pos),
levelWrapper.getMinHeight(),
DhSectionPos.getMinCornerBlockZ(pos)),
// max pos
new DhApiVec3d(DhSectionPos.getMinCornerBlockX(pos) + fogWidthInBlocks,
levelWrapper.getMaxHeight() + randomHeightModifier,
DhSectionPos.getMinCornerBlockZ(pos) + fogWidthInBlocks),
new Color(randomGrayColorValue, randomGrayColorValue, randomGrayColorValue), EDhApiBlockMaterial.UNKNOWN);
}
//================//
@@ -20,7 +20,9 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.enums.EUnexploredTerrainType;
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
import com.seibel.distanthorizons.core.file.structure.ISaveStructure;
@@ -30,6 +32,7 @@ import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRend
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import org.jetbrains.annotations.Nullable;
@@ -68,7 +71,12 @@ public interface IDhLevel extends AutoCloseable, GeneratedFullDataSourceProvider
/** @return null on server-only levels */
@Nullable
IDhApiRenderableBoxGroup getUnexploredFogRenderableBoxGroup();
IDhApiRenderableBoxGroup getUnexploredTerrainRenderableBoxGroup();
/** should only be used for client levels */
DhApiRenderableBox createUnexploredTerrainRenderableBox(long pos, IClientLevelWrapper levelWrapper);
EUnexploredTerrainType getUnexploredTerrainType();
FullDataSourceProviderV2 getFullDataProvider();
@@ -21,9 +21,7 @@ package com.seibel.distanthorizons.core.render;
import com.google.common.base.Suppliers;
import com.google.common.cache.Cache;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
@@ -51,7 +49,6 @@ import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
@@ -165,38 +162,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
this.beaconRenderHandler = this.quadTree.beaconRenderHandler;
this.beaconBeamRepo = this.level.getBeaconBeamRepo();
this.unexploredFogRenderableBox = this.createUnexploredFogRenderableBox();
this.unexploredFogRenderableBox = this.level.createUnexploredTerrainRenderableBox(this.pos, this.levelWrapper);
DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
}
private DhApiRenderableBox createUnexploredFogRenderableBox()
{
// width
float fogWidthInBlocks = (float) DhSectionPos.getBlockWidth(this.pos);
// pseudo random height (should be consistent for a given position)
int fogHeightRange = (int) ((this.level.getMaxY() - this.level.getMinY()) * 0.25);
int halfFogHeightRange = fogHeightRange / 2;
float randomHeightModifier = (float) (DhSectionPos.hashCode(this.pos) % halfFogHeightRange) - fogHeightRange;
// pseudo random color (should be consistent for a given position)
int randomColorModifier = (DhSectionPos.hashCode(this.pos) % 30) - 15;
int randomGrayColorValue = 180 + randomColorModifier;
randomGrayColorValue = MathUtil.clamp(1, randomGrayColorValue, 256); // clamp to prevent accidental out-of-range colors
return new DhApiRenderableBox(
// min pos
new DhApiVec3d(DhSectionPos.getMinCornerBlockX(this.pos),
this.level.getMinY(),
DhSectionPos.getMinCornerBlockZ(this.pos)),
// max pos
new DhApiVec3d(DhSectionPos.getMinCornerBlockX(this.pos) + fogWidthInBlocks,
this.level.getMaxY() + randomHeightModifier,
DhSectionPos.getMinCornerBlockZ(this.pos) + fogWidthInBlocks),
new Color(randomGrayColorValue, randomGrayColorValue, randomGrayColorValue), EDhApiBlockMaterial.UNKNOWN);
}
@@ -682,7 +651,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
private void startRenderingUnexploredFog()
{
IDhApiRenderableBoxGroup boxGroup = this.level.getUnexploredFogRenderableBoxGroup();
IDhApiRenderableBoxGroup boxGroup = this.level.getUnexploredTerrainRenderableBoxGroup();
if (boxGroup != null) // box group will be null for server levels, that shouldn't be a problem here, but just in case
{
boxGroup.add(this.unexploredFogRenderableBox);
@@ -692,7 +661,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
private void stopRenderingUnexploredFog()
{
IDhApiRenderableBoxGroup boxGroup = this.level.getUnexploredFogRenderableBoxGroup();
IDhApiRenderableBoxGroup boxGroup = this.level.getUnexploredTerrainRenderableBoxGroup();
if (boxGroup != null) // box group will be null for server levels, that shouldn't be a problem here, but just in case
{
if (boxGroup.remove(this.unexploredFogRenderableBox))
@@ -37,6 +37,8 @@ public interface IClientLevelWrapper extends ILevelWrapper
int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState);
/** @return -1 if there was a problem getting the color */
int getDirtBlockColor();
/** @return -1 if there was a problem getting the color */
int getWaterBlockColor();
void clearBlockColorCache();
/** Will return null if there was an issue finding the biome. */
@@ -66,6 +66,8 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
@Override
default int getMinHeight() { return 0; }
int getSeaLevel();
default IChunkWrapper tryGetChunk(DhChunkPos pos) { return null; }
boolean hasChunkLoaded(int chunkX, int chunkZ);