From 3cef8b9a4f266ad8ab07d9dd548f3289f7c91826 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 21 Jul 2024 17:27:17 -0500 Subject: [PATCH] Improve cave culling and add config for ignored/cave blocks --- .../distanthorizons/core/config/Config.java | 50 ++++++- .../ColumnRenderBufferBuilder.java | 21 +-- .../render/bufferBuilding/LodQuadBuilder.java | 8 +- .../FullDataToRenderDataTransformer.java | 125 ++++++++++++------ .../wrapperInterfaces/IWrapperFactory.java | 11 ++ .../assets/distanthorizons/lang/en_us.json | 9 +- 6 files changed, 157 insertions(+), 67 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 0ace849e4..56a3cde8e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -20,17 +20,20 @@ package com.seibel.distanthorizons.core.config; +import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.enums.config.*; import com.seibel.distanthorizons.api.enums.config.quickOptions.*; import com.seibel.distanthorizons.api.enums.rendering.*; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode; import com.seibel.distanthorizons.core.config.eventHandlers.*; import com.seibel.distanthorizons.core.config.eventHandlers.presets.*; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.*; import com.seibel.distanthorizons.core.config.types.enums.*; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.util.StringUtil; @@ -610,6 +613,7 @@ public class Config + " does not have a ceiling.") .build(); + @Deprecated public static ConfigEntry caveCullingHeight = new ConfigEntry.Builder() .setMinDefaultMax(-4096, 40, 4096) .comment("" @@ -843,13 +847,47 @@ public class Config + "") .build(); - //public static ConfigEntry showMigrationChatWarning = new ConfigEntry.Builder() - // .set(true) - // .comment("" - // + "Determines if a message should be displayed in the chat when LOD migration starts. \n" - // + "") - // .build(); + public static ConfigEntry ignoredRenderBlockCsv = new ConfigEntry.Builder() + .set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire") + .comment("" + + "A comma separated list of block resource locations that won't be rendered by DH. \n" + + "Note: air is always included in this list. \n" + + "") + .build(); + public static ConfigEntry ignoredRenderCaveBlockCsv = new ConfigEntry.Builder() + .set("minecraft:glow_lichen,minecraft:rail,minecraft:water,minecraft:lava,minecraft:bubble_column") + .comment("" + + "A comma separated list of block resource locations that shouldn't be rendered \n" + + "if they are in a 0 sky light underground area. \n" + + "Note: air is always included in this list. \n" + + "") + .build(); + + static + { + ignoredRenderBlockCsv.addListener(new ConfigChangeListener(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv, + (blockCsv) -> + { + IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); + if (wrapperFactory != null) + { + wrapperFactory.resetRendererIgnoredBlocksSet(); + DhApi.Delayed.renderProxy.clearRenderDataCache(); + } + })); + + ignoredRenderCaveBlockCsv.addListener(new ConfigChangeListener(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv, + (blockCsv) -> + { + IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); + if (wrapperFactory != null) + { + wrapperFactory.resetRendererIgnoredCaveBlocks(); + DhApi.Delayed.renderProxy.clearRenderDataCache(); + } + })); + } } public static class Multiplayer diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index 623198fff..64c1abd02 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -82,28 +82,9 @@ public class ColumnRenderBufferBuilder { boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled; - //EVENT_LOGGER.trace("RenderRegion start QuadBuild @ " + renderSource.sectionPos); - boolean enableSkyLightCulling = - Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get() - && ( - // dimensions with a ceiling will be all caves so we don't want cave culling - !clientLevel.getLevelWrapper().hasCeiling() - // the end has a lot of overhangs with 0 lighting above the void, which look broken with - // the current cave culling logic (this could probably be improved, but just skipping it works best for now) - && !clientLevel.getLevelWrapper().getDimensionType().isTheEnd() - // FIXME temporary fix - // Cave culling is currently broken for any detail level above 0 - && DhSectionPos.getDetailLevel(renderSource.pos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL - ); - - int skyLightCullingBelow = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get(); - // FIXME: Clamp also to the max world height. - skyLightCullingBelow = Math.max(skyLightCullingBelow, clientLevel.getMinY()); - - long builderStartTime = System.currentTimeMillis(); - LodQuadBuilder builder = new LodQuadBuilder(enableSkyLightCulling, (short) (skyLightCullingBelow - clientLevel.getMinY()), enableTransparency, clientLevel.getClientLevelWrapper()); + LodQuadBuilder builder = new LodQuadBuilder(enableTransparency, clientLevel.getClientLevelWrapper()); makeLodRenderData(builder, renderSource, adjData); long builderEndTime = System.currentTimeMillis(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java index 08a593656..ab3605bdd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java @@ -51,7 +51,9 @@ public class LodQuadBuilder private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + @Deprecated public final boolean skipQuadsWithZeroSkylight; + @Deprecated public final short skyLightCullingBelow; @SuppressWarnings("unchecked") @@ -123,7 +125,7 @@ public class LodQuadBuilder // constructor // //=============// - public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow, boolean doTransparency, IClientLevelWrapper clientLevelWrapper) + public LodQuadBuilder(boolean doTransparency, IClientLevelWrapper clientLevelWrapper) { this.doTransparency = doTransparency; for (int i = 0; i < 6; i++) @@ -132,8 +134,8 @@ public class LodQuadBuilder this.transparentQuads[i] = new ArrayList<>(); } - this.skipQuadsWithZeroSkylight = enableSkylightCulling; - this.skyLightCullingBelow = skyLightCullingBelow; + this.skipQuadsWithZeroSkylight = false; + this.skyLightCullingBelow = 0; this.clientLevelWrapper = clientLevelWrapper; this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRendering.get(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index dc2f05229..bbfa76a0b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.dataObjects.transformers; import com.seibel.distanthorizons.api.enums.config.EDhApiBlocksToAvoid; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -110,32 +111,23 @@ public class FullDataToRenderDataTransformer } columnSource.markNotEmpty(); + int baseX = DhSectionPos.getMinCornerBlockX(pos); + int baseZ = DhSectionPos.getMinCornerBlockZ(pos); - if (dataDetail == columnSource.getDataDetailLevel()) + for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++) { - int baseX = DhSectionPos.getMinCornerBlockX(pos); - int baseZ = DhSectionPos.getMinCornerBlockZ(pos); - - for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++) + for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++) { - for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++) - { - throwIfThreadInterrupted(); - - ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); - LongArrayList dataColumn = fullDataSource.get(x, z); - updateRenderDataViewWithFullDataColumn(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn); - } + throwIfThreadInterrupted(); + + ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); + LongArrayList dataColumn = fullDataSource.get(x, z); + updateRenderDataViewWithFullDataColumn(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn); } - - columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); - - } - else - { - throw new UnsupportedOperationException("To be implemented"); - //FIXME: Implement different size creation of renderData } + + columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); + return columnSource; } @@ -168,19 +160,36 @@ public class FullDataToRenderDataTransformer int blockX, int blockZ, ColumnArrayView renderColumnData, LongArrayList fullColumnData) { - boolean avoidSolidBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING); + boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING); boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get(); HashSet blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper()); + HashSet caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(level.getLevelWrapper()); + + boolean caveCullingEnabled = + Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get() + && ( + // dimensions with a ceiling will be all caves so we don't want cave culling + !level.getLevelWrapper().hasCeiling() + // the end has a lot of overhangs with 0 lighting above the void, which look broken with + // the current cave culling logic (this could probably be improved, but just skipping it works best for now) + && !level.getLevelWrapper().getDimensionType().isTheEnd() + ); boolean isVoid = true; + int colorToApplyToNextBlock = -1; int lastColor = 0; int lastBottom = -10000; + int skylightToApplyToNextBlock = -1; int blocklightToApplyToNextBlock = -1; int columnOffset = 0; + IBiomeWrapper biome = null; + IBlockStateWrapper block = null; + + // goes from the top down for (int i = 0; i < fullColumnData.size(); i++) { @@ -191,8 +200,6 @@ public class FullDataToRenderDataTransformer int blockLight = FullDataPointUtil.getBlockLight(fullData); int skyLight = FullDataPointUtil.getSkyLight(fullData); - IBiomeWrapper biome; - IBlockStateWrapper block; try { biome = fullDataMapping.getBiomeWrapper(id); @@ -217,28 +224,72 @@ public class FullDataToRenderDataTransformer } - if (blockStatesToIgnore.contains(block)) + //====================// + // ignored block and // + // cave culling check // + //====================// + + boolean ignoreBlock = blockStatesToIgnore.contains(block); + boolean caveBlock = caveBlockStatesToIgnore.contains(block); + if (caveBlock) { - // Don't render: air, barriers, light blocks, etc. + if (caveCullingEnabled + // assume this data point is underground if it has no sky-light + && skyLight == LodUtil.MIN_MC_LIGHT + // cave culling shouldn't happen when at the top of the world + && columnOffset != 0 + // cave culling can't happen when at the bottom of the world + && columnOffset != fullColumnData.size()) + { + // we need to get the next sky/block lights because + // the air block here will always have a light of 0/0 due to only the top of the LOD's light being saved. + long nextFullData = fullColumnData.getLong(i+1); + int nextSkyLight = FullDataPointUtil.getSkyLight(nextFullData); + + if (nextSkyLight == LodUtil.MIN_MC_LIGHT + && ColorUtil.getAlpha(lastColor) == 255) + { + // replace the previous block with new bottom + long columnData = renderColumnData.get(columnOffset - 1); + columnData = RenderDataPointUtil.setYMin(columnData, bottomY); + renderColumnData.set(columnOffset - 1, columnData); + } + + continue; + } + + + if (ignoreBlock) + { + // this is a merged block and a cave block, so it should never be rendered + continue; + } + } + else if (ignoreBlock) + { + // this is an ignored block, but shouldn't be merged like a cave block continue; } - // solid block check - if (avoidSolidBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE) + //===================// + // solid block check // + //===================// + + if (ignoreNonCollidingBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE) { if (colorBelowWithAvoidedBlocks) { int tempColor = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block); - if (ColorUtil.getAlpha(tempColor) == 0) + // don't transfer the color when alpha is 0 + if (ColorUtil.getAlpha(tempColor) != 0) { - //make sure to not transfer the color when alpha is 0 - continue; + // don't transfer alpha if for some reason grass is semi transparent + colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255); + + skylightToApplyToNextBlock = skyLight; + blocklightToApplyToNextBlock = blockLight; } - //mare sure to not trnasfer alpha if for some reason grass is semi transparent - colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255); - skylightToApplyToNextBlock = skyLight; - blocklightToApplyToNextBlock = blockLight; } // don't add this block @@ -261,10 +312,10 @@ public class FullDataToRenderDataTransformer blockLight = blocklightToApplyToNextBlock; } - //check if they share a top-bottom face and if they have same collor + //check if they share a top-bottom face and if they have same color if (color == lastColor && bottomY + blockHeight == lastBottom && columnOffset > 0) { - //replace the previus block with new bottom + //replace the previous block with new bottom long columnData = renderColumnData.get(columnOffset - 1); columnData = RenderDataPointUtil.setYMin(columnData, bottomY); renderColumnData.set(columnOffset - 1, columnData); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index 139949cc4..427310ece 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -82,6 +82,17 @@ public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable * Generally this contains blocks like: air, barriers, light blocks, etc. */ HashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper); + /** + * Returns the set of {@link IBlockStateWrapper}'s that shouldn't be rendered in caves.
+ * Generally this contains blocks like: air, rails, glow lichen, etc. + */ + HashSet getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper); + + /** clears the cached values */ + void resetRendererIgnoredCaveBlocks(); + /** clears the cached values */ + void resetRendererIgnoredBlocksSet(); + /** * Specifically designed to be used with the API. diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 9ea4fe0c4..f003bd700 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -379,7 +379,14 @@ "How should block data be compressed when creating LOD data? \nThis setting will only affect new or updated LOD data, \nany data already generated when this setting is changed will be \nunaffected until it is modified or re-loaded. \n\nMost Accurate: Merge Same Blocks \nHighest Compression: Visually Equal", "distanthorizons.config.client.advanced.lodBuilding.showMigrationChatWarning": "Log Migration In Chat", - + "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderBlockCsv": + "Ignored Block CSV", + "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderBlockCsv.@tooltip": + "A comma separated list of block resource locations that won't be rendered by DH. \nNote: air is always included in this list.", + "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderCaveBlockCsv": + "Ignored Cave Block CSV", + "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderCaveBlockCsv.@tooltip": + "A comma separated list of block resource locations that shouldn't be rendered \nif they are in a 0 sky light underground area. \nNote: air is always included in this list.", "distanthorizons.config.client.advanced.multiplayer": "Multiplayer",