From e2bdfcc2da21affd1e270bd5eb9874c20d7e5eb5 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 21 Nov 2021 15:28:59 -0600 Subject: [PATCH] Initial implementation of Core --- .gitmodules | 3 + core | 1 + src/main/java/com/seibel/lod/ModInfo.java | 35 - .../bufferBuilding/LodBufferBuilder.java | 971 ------------------ .../lodTemplates/AbstractLodTemplate.java | 53 - .../bufferBuilding/lodTemplates/Box.java | 595 ----------- .../lodTemplates/CubicLodTemplate.java | 142 --- .../lodTemplates/DynamicLodTemplate.java | 48 - .../lodTemplates/TriangularLodTemplate.java | 46 - .../lod/builders/lodBuilding/LodBuilder.java | 535 ---------- .../lodBuilding/LodBuilderConfig.java | 95 -- .../worldGeneration/LodGenWorker.java | 705 ------------- .../worldGeneration/LodWorldGenerator.java | 204 ---- .../java/com/seibel/lod/config/LodConfig.java | 591 ----------- .../com/seibel/lod/enums/BlockToAvoid.java | 47 - .../seibel/lod/enums/BufferRebuildTimes.java | 49 - .../lod/enums/DistanceGenerationMode.java | 95 -- .../com/seibel/lod/enums/FogDistance.java | 38 - .../com/seibel/lod/enums/FogDrawOverride.java | 47 - .../seibel/lod/enums/GenerationPriority.java | 37 - .../com/seibel/lod/enums/GlProxyContext.java | 38 - .../com/seibel/lod/enums/GpuUploadMethod.java | 38 - .../seibel/lod/enums/HorizontalQuality.java | 53 - .../lod/enums/HorizontalResolution.java | 175 ---- .../com/seibel/lod/enums/HorizontalScale.java | 49 - .../com/seibel/lod/enums/LodTemplate.java | 62 -- .../com/seibel/lod/enums/ShadingMode.java | 22 - .../com/seibel/lod/enums/VanillaOverdraw.java | 45 - .../com/seibel/lod/enums/VerticalQuality.java | 80 -- .../seibel/lod/forge/ForgeClientProxy.java | 105 ++ .../com/seibel/lod/forge/ForgeConfig.java | 437 ++++++++ .../{LodMain.java => forge/ForgeMain.java} | 28 +- .../mixins}/MixinWorldRenderer.java | 22 +- .../forge/wrappers/ForgeDependencySetup.java | 37 + .../wrappers/McObjectConverter.java} | 52 +- .../lod/forge/wrappers/WrapperFactory.java | 91 ++ .../wrappers/WrapperUtil.java} | 16 +- .../block/BlockColorSingletonWrapper.java} | 32 +- .../wrappers/block}/BlockColorWrapper.java | 73 +- .../forge/wrappers/block/BlockPosWrapper.java | 102 ++ .../wrappers/block}/BlockShapeWrapper.java | 51 +- .../forge/wrappers/chunk/ChunkPosWrapper.java | 135 +++ .../forge/wrappers/chunk/ChunkWrapper.java | 125 +++ .../config/LodConfigWrapperSingleton.java | 491 +++++++++ .../minecraft/MinecraftRenderWrapper.java | 139 +++ .../wrappers/minecraft}/MinecraftWrapper.java | 173 +++- .../wrappers/minecraft/ProfilerWrapper.java | 43 + .../forge/wrappers/misc/LightMapWrapper.java | 31 + .../world/BiomeColorWrapperSingleton.java | 60 ++ .../wrappers/world}/BiomeWrapper.java | 30 +- .../wrappers/world}/DimensionTypeWrapper.java | 31 +- .../forge/wrappers/world/WorldWrapper.java | 151 +++ .../worldGeneration/LodServerWorld.java | 17 +- .../WorldGeneratorWrapper.java | 389 +++++++ .../com/seibel/lod/handlers/ChunkLoader.java | 74 -- .../lod/handlers/LodDimensionFileHandler.java | 423 -------- .../lod/handlers/ReflectionHandler.java | 164 --- .../seibel/lod/objects/LevelContainer.java | 115 --- .../com/seibel/lod/objects/LodDimension.java | 905 ---------------- .../com/seibel/lod/objects/LodRegion.java | 609 ----------- .../java/com/seibel/lod/objects/LodWorld.java | 173 ---- .../lod/objects/NearFarFogSettings.java | 64 -- .../lod/objects/PosToGenerateContainer.java | 210 ---- .../lod/objects/PosToRenderContainer.java | 147 --- .../com/seibel/lod/objects/RegionPos.java | 90 -- .../lod/objects/VerticalLevelContainer.java | 240 ----- .../com/seibel/lod/proxy/ClientProxy.java | 397 ------- .../java/com/seibel/lod/proxy/GlProxy.java | 244 ----- .../com/seibel/lod/render/LodRenderer.java | 959 ----------------- .../com/seibel/lod/render/RenderUtil.java | 124 --- .../java/com/seibel/lod/util/ColorUtil.java | 115 --- .../com/seibel/lod/util/DataPointUtil.java | 520 ---------- .../seibel/lod/util/DetailDistanceUtil.java | 171 --- .../com/seibel/lod/util/LevelPosUtil.java | 269 ----- .../java/com/seibel/lod/util/LodUtil.java | 501 --------- .../com/seibel/lod/util/ThreadMapUtil.java | 217 ---- .../lod/wrappers/Block/BlockPosWrapper.java | 67 -- .../lod/wrappers/Chunk/ChunkGenerator.java | 7 - .../lod/wrappers/Chunk/ChunkPosWrapper.java | 92 -- .../lod/wrappers/Chunk/ChunkWrapper.java | 83 -- .../seibel/lod/wrappers/LightMapWrapper.java | 19 - .../wrappers/Vertex/BufferBuilderWrapper.java | 5 - .../wrappers/Vertex/VertexBufferWrapper.java | 5 - .../lod/wrappers/World/BiomeColorWrapper.java | 24 - .../lod/wrappers/World/LevelWrapper.java | 80 -- .../lod/wrappers/World/WorldLightWrapper.java | 7 - src/main/resources/lod.mixins.json | 2 +- 87 files changed, 2704 insertions(+), 12178 deletions(-) create mode 100644 .gitmodules create mode 160000 core delete mode 100644 src/main/java/com/seibel/lod/ModInfo.java delete mode 100644 src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java delete mode 100644 src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/Box.java delete mode 100644 src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java delete mode 100644 src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilderConfig.java delete mode 100644 src/main/java/com/seibel/lod/builders/worldGeneration/LodGenWorker.java delete mode 100644 src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java delete mode 100644 src/main/java/com/seibel/lod/config/LodConfig.java delete mode 100644 src/main/java/com/seibel/lod/enums/BlockToAvoid.java delete mode 100644 src/main/java/com/seibel/lod/enums/BufferRebuildTimes.java delete mode 100644 src/main/java/com/seibel/lod/enums/DistanceGenerationMode.java delete mode 100644 src/main/java/com/seibel/lod/enums/FogDistance.java delete mode 100644 src/main/java/com/seibel/lod/enums/FogDrawOverride.java delete mode 100644 src/main/java/com/seibel/lod/enums/GenerationPriority.java delete mode 100644 src/main/java/com/seibel/lod/enums/GlProxyContext.java delete mode 100644 src/main/java/com/seibel/lod/enums/GpuUploadMethod.java delete mode 100644 src/main/java/com/seibel/lod/enums/HorizontalQuality.java delete mode 100644 src/main/java/com/seibel/lod/enums/HorizontalResolution.java delete mode 100644 src/main/java/com/seibel/lod/enums/HorizontalScale.java delete mode 100644 src/main/java/com/seibel/lod/enums/LodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/enums/ShadingMode.java delete mode 100644 src/main/java/com/seibel/lod/enums/VanillaOverdraw.java delete mode 100644 src/main/java/com/seibel/lod/enums/VerticalQuality.java create mode 100644 src/main/java/com/seibel/lod/forge/ForgeClientProxy.java create mode 100644 src/main/java/com/seibel/lod/forge/ForgeConfig.java rename src/main/java/com/seibel/lod/{LodMain.java => forge/ForgeMain.java} (79%) rename src/main/java/com/seibel/lod/{mixin => forge/mixins}/MixinWorldRenderer.java (74%) create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java rename src/main/java/com/seibel/lod/{enums/DebugMode.java => forge/wrappers/McObjectConverter.java} (52%) create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/WrapperFactory.java rename src/main/java/com/seibel/lod/{enums/FogQuality.java => forge/wrappers/WrapperUtil.java} (68%) rename src/main/java/com/seibel/lod/{util/LodThreadFactory.java => forge/wrappers/block/BlockColorSingletonWrapper.java} (54%) rename src/main/java/com/seibel/lod/{wrappers/Block => forge/wrappers/block}/BlockColorWrapper.java (82%) create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/block/BlockPosWrapper.java rename src/main/java/com/seibel/lod/{wrappers/Block => forge/wrappers/block}/BlockShapeWrapper.java (70%) create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/chunk/ChunkPosWrapper.java create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/chunk/ChunkWrapper.java create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/config/LodConfigWrapperSingleton.java create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/minecraft/MinecraftRenderWrapper.java rename src/main/java/com/seibel/lod/{wrappers => forge/wrappers/minecraft}/MinecraftWrapper.java (60%) create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/minecraft/ProfilerWrapper.java create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/misc/LightMapWrapper.java create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/world/BiomeColorWrapperSingleton.java rename src/main/java/com/seibel/lod/{wrappers/World => forge/wrappers/world}/BiomeWrapper.java (86%) rename src/main/java/com/seibel/lod/{wrappers/World => forge/wrappers/world}/DimensionTypeWrapper.java (67%) create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/world/WorldWrapper.java rename src/main/java/com/seibel/lod/{builders => forge/wrappers}/worldGeneration/LodServerWorld.java (94%) create mode 100644 src/main/java/com/seibel/lod/forge/wrappers/worldGeneration/WorldGeneratorWrapper.java delete mode 100644 src/main/java/com/seibel/lod/handlers/ChunkLoader.java delete mode 100644 src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java delete mode 100644 src/main/java/com/seibel/lod/handlers/ReflectionHandler.java delete mode 100644 src/main/java/com/seibel/lod/objects/LevelContainer.java delete mode 100644 src/main/java/com/seibel/lod/objects/LodDimension.java delete mode 100644 src/main/java/com/seibel/lod/objects/LodRegion.java delete mode 100644 src/main/java/com/seibel/lod/objects/LodWorld.java delete mode 100644 src/main/java/com/seibel/lod/objects/NearFarFogSettings.java delete mode 100644 src/main/java/com/seibel/lod/objects/PosToGenerateContainer.java delete mode 100644 src/main/java/com/seibel/lod/objects/PosToRenderContainer.java delete mode 100644 src/main/java/com/seibel/lod/objects/RegionPos.java delete mode 100644 src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java delete mode 100644 src/main/java/com/seibel/lod/proxy/ClientProxy.java delete mode 100644 src/main/java/com/seibel/lod/proxy/GlProxy.java delete mode 100644 src/main/java/com/seibel/lod/render/LodRenderer.java delete mode 100644 src/main/java/com/seibel/lod/render/RenderUtil.java delete mode 100644 src/main/java/com/seibel/lod/util/ColorUtil.java delete mode 100644 src/main/java/com/seibel/lod/util/DataPointUtil.java delete mode 100644 src/main/java/com/seibel/lod/util/DetailDistanceUtil.java delete mode 100644 src/main/java/com/seibel/lod/util/LevelPosUtil.java delete mode 100644 src/main/java/com/seibel/lod/util/LodUtil.java delete mode 100644 src/main/java/com/seibel/lod/util/ThreadMapUtil.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/Block/BlockPosWrapper.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/Chunk/ChunkGenerator.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/Chunk/ChunkPosWrapper.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/Chunk/ChunkWrapper.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/LightMapWrapper.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/Vertex/BufferBuilderWrapper.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/Vertex/VertexBufferWrapper.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/World/BiomeColorWrapper.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/World/LevelWrapper.java delete mode 100644 src/main/java/com/seibel/lod/wrappers/World/WorldLightWrapper.java diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..cd75fd658 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "core"] + path = core + url = git@gitlab.com:jeseibel/distant-horizons-core.git diff --git a/core b/core new file mode 160000 index 000000000..ace3c0301 --- /dev/null +++ b/core @@ -0,0 +1 @@ +Subproject commit ace3c03019d20fcd2456a7ea3ccb8d24c6fc51ff diff --git a/src/main/java/com/seibel/lod/ModInfo.java b/src/main/java/com/seibel/lod/ModInfo.java deleted file mode 100644 index 8418b46a8..000000000 --- a/src/main/java/com/seibel/lod/ModInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod; - -/** - * This file is similar to mcmod.info - * @author James Seibel - * @version 10-23-2021 - */ -public final class ModInfo -{ - public static final String ID = "lod"; - public static final String NAME = "DistantHorizons"; - /** Human readable version of MOD_NAME */ - public static final String READABLE_NAME = "Distant Horizons"; - public static final String API = "LodAPI"; - public static final String VERSION = "a1.5.2"; -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java deleted file mode 100644 index 4d27c340b..000000000 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java +++ /dev/null @@ -1,971 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.bufferBuilding; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.locks.ReentrantLock; - -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL30; -import org.lwjgl.opengl.GL45; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.mojang.blaze3d.systems.RenderSystem; -import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box; -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.GlProxyContext; -import com.seibel.lod.enums.GpuUploadMethod; -import com.seibel.lod.enums.VanillaOverdraw; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.LodRegion; -import com.seibel.lod.objects.PosToRenderContainer; -import com.seibel.lod.objects.RegionPos; -import com.seibel.lod.proxy.ClientProxy; -import com.seibel.lod.proxy.GlProxy; -import com.seibel.lod.render.LodRenderer; -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; -import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.DetailDistanceUtil; -import com.seibel.lod.util.LevelPosUtil; -import com.seibel.lod.util.LodThreadFactory; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.util.ThreadMapUtil; -import com.seibel.lod.wrappers.MinecraftWrapper; - -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.vertex.VertexBuffer; -import net.minecraft.util.Direction; - -/** - * This object is used to create NearFarBuffer objects. - * @author James Seibel - * @version 10-23-2021 - */ -public class LodBufferBuilder -{ - - /** The thread used to generate new LODs off the main thread. */ - public static final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilder.class.getSimpleName() + " - main")); - /** The threads used to generate buffers. */ - public static final ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.advancedModOptions.threading.numberOfBufferBuilderThreads.get(), new ThreadFactoryBuilder().setNameFormat("Buffer-Builder-%d").build()); - - /** - * When uploading to a buffer that is too small, - * recreate it this many times bigger than the upload payload - */ - public static final double BUFFER_EXPANSION_MULTIPLIER = 1.5; - - /** - * When buffers are first created they are allocated to this size (in Bytes). - * This size will be too small, more than likely. The buffers will be expanded - * when need be to fit the larger sizes. - */ - public static final int DEFAULT_MEMORY_ALLOCATION = 1024; - - public static int skyLightPlayer = 15; - - /** - * How many buffers there are for the given region.
- * This is done because some regions may require more memory than - * can be directly allocated, so we split the regions into smaller sections.
- * This keeps track of those sections. - */ - public volatile int[][] numberOfBuffersPerRegion; - - /** Stores the vertices when building the VBOs */ - public volatile BufferBuilder[][][] buildableBuffers; - - /** The OpenGL IDs of the storage buffers used by the buildableVbos */ - public int[][][] buildableStorageBufferIds; - /** The OpenGL IDs of the storage buffers used by the drawableVbos */ - public int[][][] drawableStorageBufferIds; - - /** Used when building new VBOs */ - public volatile VertexBuffer[][][] buildableVbos; - /** VBOs that are sent over to the LodNodeRenderer */ - public volatile VertexBuffer[][][] drawableVbos; - - /** - * if this is true the LOD buffers are currently being - * regenerated. - */ - public boolean generatingBuffers = false; - - /** - * if this is true new LOD buffers have been generated - * and are waiting to be swapped with the drawable buffers - */ - private boolean switchVbos = false; - - /** Size of the buffer builders in bytes last time we created them */ - public int previousBufferSize = 0; - - /** Width of the dimension in regions last time we created the buffers */ - public int previousRegionWidth = 0; - - /** this is used to prevent multiple threads creating, destroying, or using the buffers at the same time */ - private final ReentrantLock bufferLock = new ReentrantLock(); - - private volatile Box[][] boxCache; - private volatile PosToRenderContainer[][] setsToRender; - private volatile RegionPos center; - - /** - * This is the ChunkPosWrapper the player was at the last time the buffers were built. - * IE the center of the buffers last time they were built - */ - private volatile ChunkPosWrapper drawableCenterChunkPos = new ChunkPosWrapper(0, 0); - private volatile ChunkPosWrapper buildableCenterChunkPos = new ChunkPosWrapper(0, 0); - - - - - - - public LodBufferBuilder() - { - - } - - /** - * Create a thread to asynchronously generate LOD buffers - * centered around the given camera X and Z. - *
- * This method will write to the drawable near and far buffers. - *
- * After the buildable buffers have been generated they must be - * swapped with the drawable buffers in the LodRenderer to be drawn. - */ - public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim, - BlockPosWrapper playerBlockPos, boolean fullRegen) - { - - // only allow one generation process to happen at a time - if (generatingBuffers) - return; - - if (buildableBuffers == null) - // setupBuffers hasn't been called yet - return; - - generatingBuffers = true; - - - Thread thread = new Thread(() -> generateLodBuffersThread(renderer, lodDim, playerBlockPos, fullRegen)); - - mainGenThread.execute(thread); - } - - // this was pulled out as a separate method so that it could be - // more easily edited by hot swapping. Because, As far as James is aware - // you can't hot swap lambda expressions. - private void generateLodBuffersThread(LodRenderer renderer, LodDimension lodDim, - BlockPosWrapper playerBlockPos, boolean fullRegen) - { - bufferLock.lock(); - - try - { - // round the player's block position down to the nearest chunk BlockPos - ChunkPosWrapper playerChunkPos = new ChunkPosWrapper(playerBlockPos); - BlockPosWrapper playerBlockPosRounded = playerChunkPos.getWorldPosition(); - - - //long startTime = System.currentTimeMillis(); - - ArrayList> nodeToRenderThreads = new ArrayList<>(lodDim.getWidth() * lodDim.getWidth()); - - startBuffers(fullRegen, lodDim); - - - RegionPos playerRegionPos = new RegionPos(playerChunkPos); - if (center == null) - center = playerRegionPos; - - if (setsToRender == null) - setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()]; - - if (setsToRender.length != lodDim.getWidth()) - setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()]; - - if (boxCache == null) - boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()]; - - if (boxCache.length != lodDim.getWidth()) - boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()]; - - // this will be the center of the VBOs once they have been built - buildableCenterChunkPos = playerChunkPos; - - - //================================// - // create the nodeToRenderThreads // - //================================// - - skyLightPlayer = MinecraftWrapper.INSTANCE.getWrappedClientLevel().getSkyLight(playerBlockPos); - - for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++) - { - for (int zRegion = 0; zRegion < lodDim.getWidth(); zRegion++) - { - if (lodDim.doesRegionNeedBufferRegen(xRegion, zRegion) || fullRegen) - { - RegionPos regionPos = new RegionPos( - xRegion + lodDim.getCenterRegionPosX() - lodDim.getWidth() / 2, - zRegion + lodDim.getCenterRegionPosZ() - lodDim.getWidth() / 2); - - // local position in the vbo and bufferBuilder arrays - BufferBuilder[] currentBuffers = buildableBuffers[xRegion][zRegion]; - LodRegion region = lodDim.getRegion(regionPos.x, regionPos.z); - - if (region == null) - continue; - - // make sure the buffers weren't - // changed while we were running this method - if (currentBuffers == null || !currentBuffers[0].building()) - return; - - byte minDetail = region.getMinDetailLevel(); - - - final int xR = xRegion; - final int zR = zRegion; - - //we create the Callable to use for the buffer builder creation - Callable dataToRenderThread = () -> - { - //Variable initialization - byte detailLevel; - int posX; - int posZ; - int xAdj; - int zAdj; - int bufferIndex; - boolean posNotInPlayerChunk; - boolean adjPosInPlayerChunk; - Box box = ThreadMapUtil.getBox(); - boolean[] adjShadeDisabled = ThreadMapUtil.getAdjShadeDisabledArray(); - - // determine how many LODs we can stack vertically - int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte) 0); - - //we get or create the map that will contain the adj data - Map adjData = ThreadMapUtil.getAdjDataArray(maxVerticalData); - - //previous setToRender cache - if (setsToRender[xR][zR] == null) - setsToRender[xR][zR] = new PosToRenderContainer(minDetail, regionPos.x, regionPos.z); - - - //We ask the lod dimension which block we have to render given the player position - PosToRenderContainer posToRender = setsToRender[xR][zR]; - posToRender.clear(minDetail, regionPos.x, regionPos.z); - - lodDim.getPosToRender( - posToRender, - regionPos, - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ()); - - - - // keep a local version, so we don't have to worry about indexOutOfBounds Exceptions - // if it changes in the LodRenderer while we are working here - boolean[][] vanillaRenderedChunks = renderer.vanillaRenderedChunks; - short gameChunkRenderDistance = (short) (vanillaRenderedChunks.length / 2 - 1); - - - - for (int index = 0; index < posToRender.getNumberOfPos(); index++) - { - bufferIndex = index % currentBuffers.length; - detailLevel = posToRender.getNthDetailLevel(index); - posX = posToRender.getNthPosX(index); - posZ = posToRender.getNthPosZ(index); - - int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX(); - int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ(); - - //We don't want to render this fake block if - //The block is inside the render distance with, is not bigger than a chunk and is positioned in a chunk set as vanilla rendered - // - //The block is in the player chunk or in a chunk adjacent to the player - if(isThisPositionGoingToBeRendered(detailLevel, posX, posZ, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance)) - { - continue; - } - - //we check if the block to render is not in player chunk - posNotInPlayerChunk = !(chunkXdist == 0 && chunkZdist == 0); - - // We extract the adj data in the four cardinal direction - - // we first reset the adjShadeDisabled. This is used to disable the shade on the border when we have transparent block like water or glass - // to avoid having a "darker border" underground - Arrays.fill(adjShadeDisabled, false); - - //We check every adj block in each direction - for (Direction direction : Box.ADJ_DIRECTIONS) - { - - xAdj = posX + Box.DIRECTION_NORMAL_MAP.get(direction).getX(); - zAdj = posZ + Box.DIRECTION_NORMAL_MAP.get(direction).getZ(); - long data; - chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.getX(); - chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.getZ(); - adjPosInPlayerChunk = (chunkXdist == 0 && chunkZdist == 0); - - //If the adj block is rendered in the same region and with same detail - // and is positioned in a place that is not going to be rendered by vanilla game - // then we can set this position as adj - // We avoid cases where the adjPosition is in player chunk while the position is not - // to always have a wall underwater - if(posToRender.contains(detailLevel, xAdj, zAdj) - && !isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) - && !(posNotInPlayerChunk && adjPosInPlayerChunk)) - { - for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++) - { - data = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex); - adjShadeDisabled[Box.DIRECTION_INDEX.get(direction)] = false; - adjData.get(direction)[verticalIndex] = data; - } - } - else - { - //Otherwise, we check if this position is - data = lodDim.getSingleData(detailLevel, xAdj, zAdj); - - adjData.get(direction)[0] = DataPointUtil.EMPTY_DATA; - - if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk)) - && !DataPointUtil.isVoid(data)) - { - adjShadeDisabled[Box.DIRECTION_INDEX.get(direction)] = DataPointUtil.getAlpha(data) < 255; - } - } - } - - - // We render every vertical lod present in this position - // We only stop when we find a block that is void or non existing block - long data; - for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++) - { - - //we get the above block as adj UP - if (verticalIndex > 0) - adjData.get(Direction.UP)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex - 1); - else - adjData.get(Direction.UP)[0] = DataPointUtil.EMPTY_DATA; - - - //we get the below block as adj DOWN - if (verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ) - 1) - adjData.get(Direction.DOWN)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex + 1); - else - adjData.get(Direction.DOWN)[0] = DataPointUtil.EMPTY_DATA; - - //We extract the data to render - data = lodDim.getData(detailLevel, posX, posZ, verticalIndex); - - //If the data is not renderable (Void or non existing) we stop since there is no data left in this position - if (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data)) - break; - - //We send the call to create the vertices - LodConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.get().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, data, adjData, - detailLevel, posX, posZ, box, renderer.previousDebugMode, adjShadeDisabled); - } - - } // for pos to in list to render - // the thread executed successfully - return true; - }; - - nodeToRenderThreads.add(dataToRenderThread); - - } - } // region z - } // region z - - - //long executeStart = System.currentTimeMillis(); - // wait for all threads to finish - List> futuresBuffer = bufferBuilderThreads.invokeAll(nodeToRenderThreads); - for (Future future : futuresBuffer) - { - // the future will be false if its thread failed - if (!future.get()) - { - ClientProxy.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over."); - break; - } - } - //long executeEnd = System.currentTimeMillis(); - - - //long endTime = System.currentTimeMillis(); - //long buildTime = endTime - startTime; - //long executeTime = executeEnd - executeStart; - -// ClientProxy.LOGGER.info("Thread Build time: " + buildTime + " ms" + '\n' + -// "thread execute time: " + executeTime + " ms"); - - // mark that the buildable buffers as ready to swap - switchVbos = true; - } - catch (Exception e) - { - ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: "); - e.printStackTrace(); - } - finally - { - // clean up any potentially open resources - if (buildableBuffers != null) - closeBuffers(fullRegen, lodDim); - - try - { - // upload the new buffers - uploadBuffers(fullRegen, lodDim); - } - catch (Exception e) - { - ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" was unable to upload the buffers to the GPU: " + e.getMessage()); - e.printStackTrace(); - } - - // regardless of whether we were able to successfully create - // the buffers, we are done generating. - generatingBuffers = false; - bufferLock.unlock(); - } - } - - private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, ChunkPosWrapper playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){ - - - // skip any chunks that Minecraft is going to render - int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX(); - int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ(); - - // check if the chunk is on the border - boolean isItBorderPos; - if (LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get() == VanillaOverdraw.BORDER) - isItBorderPos = LodUtil.isBorderChunk(vanillaRenderedChunks, chunkXdist + gameChunkRenderDistance + 1, chunkZdist + gameChunkRenderDistance + 1); - else - isItBorderPos = false; - - - //boolean smallRenderDistance = gameChunkRenderDistance <= LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW; - - // get the positions that will be rendered - - return (gameChunkRenderDistance >= Math.abs(chunkXdist) - && gameChunkRenderDistance >= Math.abs(chunkZdist) - && detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL - && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]) - && (!isItBorderPos); - } - - - - - - - //===============================// - // BufferBuilder related methods // - //===============================// - - /** - * Called from the LodRenderer to create the - * BufferBuilders.

- *

- * May have to wait for the bufferLock to open. - */ - public void setupBuffers(LodDimension lodDimension) - { - GlProxy glProxy = GlProxy.getInstance(); - - bufferLock.lock(); - int numbRegionsWide = lodDimension.getWidth(); - long regionMemoryRequired; - int numberOfBuffers; - - previousRegionWidth = numbRegionsWide; - numberOfBuffersPerRegion = new int[numbRegionsWide][numbRegionsWide]; - buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide][]; - - buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][]; - drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][]; - - if (glProxy.bufferStorageSupported) - { - buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][]; - drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][]; - } - - for (int x = 0; x < numbRegionsWide; x++) - { - for (int z = 0; z < numbRegionsWide; z++) - { - regionMemoryRequired = DEFAULT_MEMORY_ALLOCATION; - - // if the memory required is greater than the max buffer - // capacity, divide the memory across multiple buffers - if (regionMemoryRequired > LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY) - { - numberOfBuffers = (int) regionMemoryRequired / LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY + 1; - - // TODO shouldn't this be determined with regionMemoryRequired? - // always allocating the max memory is a bit expensive isn't it? - regionMemoryRequired = LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY; - numberOfBuffersPerRegion[x][z] = numberOfBuffers; - buildableBuffers[x][z] = new BufferBuilder[numberOfBuffers]; - buildableVbos[x][z] = new VertexBuffer[numberOfBuffers]; - drawableVbos[x][z] = new VertexBuffer[numberOfBuffers]; - - if (glProxy.bufferStorageSupported) - { - buildableStorageBufferIds[x][z] = new int[numberOfBuffers]; - drawableStorageBufferIds[x][z] = new int[numberOfBuffers]; - } - } - else - { - // we only need one buffer for this region - numberOfBuffersPerRegion[x][z] = 1; - buildableBuffers[x][z] = new BufferBuilder[1]; - buildableVbos[x][z] = new VertexBuffer[1]; - drawableVbos[x][z] = new VertexBuffer[1]; - - if (glProxy.bufferStorageSupported) - { - buildableStorageBufferIds[x][z] = new int[1]; - drawableStorageBufferIds[x][z] = new int[1]; - } - } - - - for (int i = 0; i < numberOfBuffersPerRegion[x][z]; i++) - { - buildableBuffers[x][z][i] = new BufferBuilder((int) regionMemoryRequired); - - buildableVbos[x][z][i] = new VertexBuffer(LodUtil.LOD_VERTEX_FORMAT); - drawableVbos[x][z][i] = new VertexBuffer(LodUtil.LOD_VERTEX_FORMAT); - - - // create the initial mapped buffers (system memory) - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableVbos[x][z][i].id); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - - - if (glProxy.bufferStorageSupported) - { - // create the buffer storage (GPU memory) - buildableStorageBufferIds[x][z][i] = GL15.glGenBuffers(); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]); - GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - - drawableStorageBufferIds[x][z][i] = GL15.glGenBuffers(); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]); - GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - } - } - } - } - - bufferLock.unlock(); - } - - - - /** - * Sets the buffers and Vbos to null, forcing them to be recreated
- * and destroys any bound OpenGL objects.

- *

- * May have to wait for the bufferLock to open. - */ - public void destroyBuffers() - { - bufferLock.lock(); - - - // destroy the buffer storages if they aren't already - if (buildableStorageBufferIds != null) - { - for (int x = 0; x < buildableStorageBufferIds.length; x++) - { - for (int z = 0; z < buildableStorageBufferIds.length; z++) - { - for (int i = 0; i < buildableStorageBufferIds[x][z].length; i++) - { - int buildableId = buildableStorageBufferIds[x][z][i]; - int drawableId = drawableStorageBufferIds[x][z][i]; - - // Send this over to the render thread, if this is being - // called we aren't worried about stuttering anyway. - // This way we don't have to worry about what context this - // was called from (if any). - RenderSystem.recordRenderCall(() -> - { - GL15.glDeleteBuffers(buildableId); - GL15.glDeleteBuffers(drawableId); - }); - } - } - } - } - - buildableStorageBufferIds = null; - drawableStorageBufferIds = null; - - - - - // destroy the VBOs if they aren't already - if (buildableVbos != null) - { - for (int i = 0; i < buildableVbos.length; i++) - { - for (int j = 0; j < buildableVbos.length; j++) - { - for (int k = 0; k < buildableVbos[i][j].length; k++) - { - int buildableId; - int drawableId; - - // variables passed into a lambda expression - // need to be effectively final, so we have - // to use an else statement here - if (buildableVbos[i][j][k] != null) - buildableId = buildableVbos[i][j][k].id; - else - buildableId = 0; - - if (drawableVbos[i][j][k] != null) - drawableId = drawableVbos[i][j][k].id; - else - drawableId = 0; - - - RenderSystem.recordRenderCall(() -> - { - if (buildableId != 0) - GL15.glDeleteBuffers(buildableId); - if (drawableId != 0) - GL15.glDeleteBuffers(drawableId); - }); - } - } - } - } - - buildableVbos = null; - drawableVbos = null; - - - // these don't contain any OpenGL objects, so - // they don't require any special clean-up - buildableBuffers = null; - - bufferLock.unlock(); - } - - /** Calls begin on each of the buildable BufferBuilders. */ - private void startBuffers(boolean fullRegen, LodDimension lodDim) - { - for (int x = 0; x < buildableBuffers.length; x++) - { - for (int z = 0; z < buildableBuffers.length; z++) - { - if (fullRegen || lodDim.doesRegionNeedBufferRegen(x, z)) - { - for (int i = 0; i < buildableBuffers[x][z].length; i++) - { - // for some reason BufferBuilder.vertexCounts - // isn't reset unless this is called, which can cause - // a false indexOutOfBoundsException - buildableBuffers[x][z][i].discard(); - - buildableBuffers[x][z][i].begin(GL11.GL_QUADS, LodUtil.LOD_VERTEX_FORMAT); - } - } - } - } - } - - /** Calls end on each of the buildable BufferBuilders. */ - private void closeBuffers(boolean fullRegen, LodDimension lodDim) - { - for (int x = 0; x < buildableBuffers.length; x++) - for (int z = 0; z < buildableBuffers.length; z++) - for (int i = 0; i < buildableBuffers[x][z].length; i++) - if (buildableBuffers[x][z][i] != null && buildableBuffers[x][z][i].building() && (fullRegen || lodDim.doesRegionNeedBufferRegen(x, z))) - buildableBuffers[x][z][i].end(); - } - - - /** Upload all buildableBuffers to the GPU. */ - private void uploadBuffers(boolean fullRegen, LodDimension lodDim) - { - GlProxy glProxy = GlProxy.getInstance(); - - try - { - // make sure we are uploading to the builder context, - // this helps prevent interference (IE stuttering) with the Minecraft context. - glProxy.setGlContext(GlProxyContext.LOD_BUILDER); - - // determine the upload method - GpuUploadMethod uploadMethod = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get(); - if (!glProxy.bufferStorageSupported && uploadMethod == GpuUploadMethod.BUFFER_STORAGE) - { - // if buffer storage isn't supported - // default to SUB_DATA - LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.SUB_DATA); - uploadMethod = GpuUploadMethod.SUB_DATA; - } - - // actually upload the buffers - for (int x = 0; x < buildableVbos.length; x++) - { - for (int z = 0; z < buildableVbos.length; z++) - { - if (fullRegen || lodDim.doesRegionNeedBufferRegen(x, z)) - { - for (int i = 0; i < buildableBuffers[x][z].length; i++) - { - ByteBuffer uploadBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond(); - vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, true, uploadMethod); - lodDim.setRegenRegionBufferByArrayIndex(x, z, false); - } - } - } - } - } - catch (Exception e) - { - // this doesn't appear to be necessary anymore, but just in case. - ClientProxy.LOGGER.error(LodBufferBuilder.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage()); - e.printStackTrace(); - } - finally - { - GL15.glFinish(); - - // close the context so it can be re-used later. - // I'm guessing we can't just leave it because the executor service - // does something that invalidates the OpenGL context. - glProxy.setGlContext(GlProxyContext.NONE); - } - } - - /** Uploads the uploadBuffer so the GPU can use it. */ - private void vboUpload(VertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer, - boolean allowBufferExpansion, GpuUploadMethod uploadMethod) - { - // this shouldn't happen, but just to be safe - if (vbo.id != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER) - { - // this is how many points will be rendered - vbo.vertexCount = (uploadBuffer.capacity() / vbo.format.getVertexSize()); - - - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); - try - { - // if possible use the faster buffer storage route - if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE) - { - // get a pointer to the buffer in system memory - ByteBuffer vboBuffer = GL30.glMapBufferRange(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT); - if (vboBuffer == null) - { - int previousCapacity = uploadBuffer.capacity(); - - // only expand the buffers if the uploadBuffer actually - // has something in it and expansion is allowed - if (previousCapacity != 0 && allowBufferExpansion) - { - // the buffer(s) aren't big enough, expand them. - // This does cause lag/stuttering, so it should be avoided! - - // expand the buffer in system memory - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW); - GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); - - // un-bind the system memory buffer - GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - - // expand the buffer storage - GL15.glDeleteBuffers(storageBufferId); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, storageBufferId); - GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), 0); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - - - // recursively try to upload into the newly created buffer storage - // but don't recurse again if that fails - // (we don't want an infinitely expanding buffer!) - vboUpload(vbo, storageBufferId, uploadBuffer, false, uploadMethod); - } - } - else - { - // upload the buffer into system memory... - vboBuffer.put(uploadBuffer); - GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); - - // ...then upload into GPU memory - // (uploading into GPU memory directly can only be done - // through the glCopyBufferSubData/glCopyNamed... methods) - GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity()); - } - } - else if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING) - { - // no stuttering but high GPU usage - // stores everything in system memory instead of GPU memory - // making rendering much slower. - // Unless the user is running integrated graphics, - // in that case this will actually work better than SUB_DATA. - - - ByteBuffer vboBuffer; - - // map buffer range is better since it can be explicitly unsynchronized - if (GlProxy.getInstance().mapBufferRangeSupported) - vboBuffer = GL30.glMapBufferRange(GL30.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT); - else - vboBuffer = GL15.glMapBuffer(GL30.GL_ARRAY_BUFFER, uploadBuffer.capacity()); - - - if (vboBuffer == null) - { - GL15.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW); - GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); - } - else - { - vboBuffer.put(uploadBuffer); - } - } - else - { - // hybrid subData/bufferData // - // less stutter, low GPU usage - - //long size = GL31.glGetBufferParameteri64(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); // hopefully just a int should be long enough - long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); - if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER) - { - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW); - } - GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); - } - } - catch (Exception e) - { - ClientProxy.LOGGER.error("vboUpload failed: " + e.getClass().getSimpleName()); - e.printStackTrace(); - } - finally - { - if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING) - GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); - - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - } - - }//if vbo exists and in correct GL context - }//vboUpload - - /** Get the newly created VBOs */ - public VertexBuffersAndOffset getVertexBuffers() - { - // don't wait for the lock to open, - // since this is called on the main render thread - if (bufferLock.tryLock()) - { - VertexBuffer[][][] tmpVbo = drawableVbos; - drawableVbos = buildableVbos; - buildableVbos = tmpVbo; - - int[][][] tmpStorage = drawableStorageBufferIds; - drawableStorageBufferIds = buildableStorageBufferIds; - buildableStorageBufferIds = tmpStorage; - - drawableCenterChunkPos = buildableCenterChunkPos; - - // the vbos have been swapped - switchVbos = false; - bufferLock.unlock(); - } - - return new VertexBuffersAndOffset(drawableVbos, drawableStorageBufferIds, drawableCenterChunkPos); - } - - /** A simple container to pass multiple objects back in the getVertexBuffers method. */ - public static class VertexBuffersAndOffset - { - public final VertexBuffer[][][] vbos; - public final int[][][] storageBufferIds; - public final ChunkPosWrapper drawableCenterChunkPos; - - public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, ChunkPosWrapper newDrawableCenterChunkPos) - { - vbos = newVbos; - storageBufferIds = newStorageBufferIds; - drawableCenterChunkPos = newDrawableCenterChunkPos; - } - } - - /** - * If this is true the buildable near and far - * buffers have been generated and are ready to be - * sent to the LodRenderer. - */ - public boolean newBuffersAvailable() - { - return switchVbos; - } -} diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java deleted file mode 100644 index 0cf1eab1d..000000000 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.bufferBuilding.lodTemplates; - -import java.util.Map; - -import com.seibel.lod.enums.DebugMode; -import com.seibel.lod.util.ColorUtil; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; - -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.util.Direction; - -/** - * This is the abstract class used to create different - * BufferBuilders. - * @author James Seibel - * @version 10-10-2021 - */ -public abstract class AbstractLodTemplate -{ - - /** Uploads the given LOD to the buffer. */ - public abstract void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map adjData, - byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled); - - /** add the given position and color to the buffer */ - protected void addPosAndColor(BufferBuilder buffer, - double x, double y, double z, - int color) - { - // TODO re-add transparency by replacing the 255 with "ColorUtil.getAlpha(color)" - buffer.vertex(x, y, z).color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255).endVertex(); - } - -} diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/Box.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/Box.java deleted file mode 100644 index 1dd85c6c4..000000000 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/Box.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.bufferBuilding.lodTemplates; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.DebugMode; -import com.seibel.lod.util.ColorUtil; -import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import com.seibel.lod.wrappers.MinecraftWrapper; - -import net.minecraft.util.Direction; -import net.minecraft.util.math.vector.Vector3i; - -/** - * Similar to Minecraft's AxisAlignedBoundingBox. - * @author Leonardo Amato - * @version 10-2-2021 - */ -public class Box -{ - - public static final int ADJACENT_HEIGHT_INDEX = 0; - public static final int ADJACENT_DEPTH_INDEX = 1; - - public static final int X = 0; - public static final int Y = 1; - public static final int Z = 2; - - public static final int MIN = 0; - public static final int MAX = 1; - - public static final int VOID_FACE = 0; - - /** The six cardinal directions */ - public static final Direction[] DIRECTIONS = new Direction[] { - Direction.UP, - Direction.DOWN, - Direction.WEST, - Direction.EAST, - Direction.NORTH, - Direction.SOUTH }; - - /** North, South, East, West */ - public static final Direction[] ADJ_DIRECTIONS = new Direction[] { - Direction.EAST, - Direction.WEST, - Direction.SOUTH, - Direction.NORTH }; - - /** All the faces and vertices of a cube. This is used to extract the vertex from the column */ - public static final Map DIRECTION_VERTEX_MAP = new HashMap() - {{ - put(Direction.UP, new int[][] { - { 0, 1, 0 }, - { 0, 1, 1 }, - { 1, 1, 1 }, - { 1, 1, 0 } }); - put(Direction.DOWN, new int[][] { - { 1, 0, 0 }, - { 1, 0, 1 }, - { 0, 0, 1 }, - { 0, 0, 0 } }); - put(Direction.EAST, new int[][] { - { 1, 1, 0 }, - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 0, 0 } }); - put(Direction.WEST, new int[][] { - { 0, 0, 0 }, - { 0, 0, 1 }, - { 0, 1, 1 }, - { 0, 1, 0 } }); - put(Direction.SOUTH, new int[][] { - { 1, 0, 1 }, - { 1, 1, 1 }, - { 0, 1, 1 }, - { 0, 0, 1 } }); - put(Direction.NORTH, new int[][] { - { 0, 0, 0 }, - { 0, 1, 0 }, - { 1, 1, 0 }, - { 1, 0, 0 } }); - }}; - - - /** - * This indicates which position is invariable in the DIRECTION_VERTEX_MAP. - * Is used to extract the vertex - */ - public static final Map FACE_DIRECTION = new HashMap() - {{ - put(Direction.UP, new int[] { Y, MAX }); - put(Direction.DOWN, new int[] { Y, MIN }); - put(Direction.EAST, new int[] { X, MAX }); - put(Direction.WEST, new int[] { X, MIN }); - put(Direction.SOUTH, new int[] { Z, MAX }); - put(Direction.NORTH, new int[] { Z, MIN }); - }}; - - - /** - * This is a map from Direction to the relative normal vector - * we are using this since I'm not sure if the getNormal create new object at every call - */ - public static final Map DIRECTION_NORMAL_MAP = new HashMap() - {{ - put(Direction.UP, Direction.UP.getNormal()); - put(Direction.DOWN, Direction.DOWN.getNormal()); - put(Direction.EAST, Direction.EAST.getNormal()); - put(Direction.WEST, Direction.WEST.getNormal()); - put(Direction.SOUTH, Direction.SOUTH.getNormal()); - put(Direction.NORTH, Direction.NORTH.getNormal()); - }}; - - /** We use this index for all array that are going to */ - public static final Map DIRECTION_INDEX = new HashMap() - {{ - put(Direction.UP, 0); - put(Direction.DOWN, 1); - put(Direction.EAST, 2); - put(Direction.WEST, 3); - put(Direction.SOUTH, 4); - put(Direction.NORTH, 5); - }}; - - public static final Map ADJ_DIRECTION_INDEX = new HashMap() - {{ - put(Direction.EAST, 0); - put(Direction.WEST, 1); - put(Direction.SOUTH, 2); - put(Direction.NORTH, 3); - }}; - /** holds the box's x, y, z offset */ - public final int[] boxOffset; - /** holds the box's x, y, z width */ - public final int[] boxWidth; - - /** Holds each direction's color */ - public final int[] colorMap; - /** The original color (before shading) of this box */ - public int color; - /** - * - */ - public final Map adjHeight; - public final Map adjDepth; - public final Map skyLights; - public byte blockLight; - - /** Holds if the given direction should be culled or not */ - public final boolean[] culling; - - - /** creates an empty box */ - public Box() - { - boxOffset = new int[3]; - boxWidth = new int[3]; - - colorMap = new int[6]; - skyLights = new HashMap() - {{ - put(Direction.UP, new byte[1]); - put(Direction.DOWN, new byte[1]); - put(Direction.EAST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.WEST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.SOUTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.NORTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - }}; - adjHeight = new HashMap() - {{ - put(Direction.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - }}; - adjDepth = new HashMap() - {{ - put(Direction.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(Direction.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - }}; - - culling = new boolean[6]; - } - - /** Set the light of the columns */ - public void setLights(int skyLight, int blockLight) - { - this.blockLight = (byte) blockLight; - skyLights.get(Direction.UP)[0] = (byte) skyLight; - } - - /** - * Set the color of the columns - * @param color color to add - * @param adjShadeDisabled this array indicates which face does not need shading - */ - public void setColor(int color, boolean[] adjShadeDisabled) - { - this.color = color; - for (Direction direction : DIRECTIONS) - { - if (!adjShadeDisabled[DIRECTION_INDEX.get(direction)]) - colorMap[DIRECTION_INDEX.get(direction)] = ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientLevel().getShade(direction, true)); - else - colorMap[DIRECTION_INDEX.get(direction)] = color; - } - } - - /** - * @param direction of the face of which we want to get the color - * @return color of the face - */ - public int getColor(Direction direction) - { - if (LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get() != DebugMode.SHOW_DETAIL) - return colorMap[DIRECTION_INDEX.get(direction)]; - else - return ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientLevel().getShade(direction, true)); - } - - /** - */ - public byte getSkyLight(Direction direction, int verticalIndex) - { - if(direction == Direction.UP || direction == Direction.DOWN) - return skyLights.get(direction)[0]; - else - return skyLights.get(direction)[verticalIndex]; - } - - /** - */ - public int getBlockLight() - { - return blockLight; - } - /** clears this box, resetting everything to default values */ - public void reset() - { - Arrays.fill(boxWidth, 0); - Arrays.fill(boxOffset, 0); - Arrays.fill(colorMap, 0); - blockLight = 0; - for (Direction direction : ADJ_DIRECTIONS) - { - for (int i = 0; i < adjHeight.get(direction).length; i++) - { - adjHeight.get(direction)[i] = VOID_FACE; - adjDepth.get(direction)[i] = VOID_FACE; - skyLights.get(direction)[i] = 0; - } - } - } - - /** determine which faces should be culled */ - public void setUpCulling(int cullingDistance, BlockPosWrapper playerPos) - { - for (Direction direction : DIRECTIONS) - { - if (direction == Direction.DOWN || direction == Direction.WEST || direction == Direction.NORTH) - culling[DIRECTION_INDEX.get(direction)] = playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance; - - else if (direction == Direction.UP || direction == Direction.EAST || direction == Direction.SOUTH) - culling[DIRECTION_INDEX.get(direction)] = playerPos.get(direction.getAxis()) < getFacePos(direction) - cullingDistance; - - culling[DIRECTION_INDEX.get(direction)] = false; - } - } - - /** - * @param direction direction that we want to check if it's culled - * @return true if and only if the face of the direction is culled - */ - public boolean isCulled(Direction direction) - { - return culling[DIRECTION_INDEX.get(direction)]; - } - - - /** - * This method create all the shared face culling based on the adjacent data - * @param adjData data adjacent to the column we are going to render - */ - public void setAdjData(Map adjData) - { - int height; - int depth; - int minY = getMinY(); - int maxY = getMaxY(); - long singleAdjDataPoint; - - /* TODO implement attached vertical face culling - //Up direction case - if(DataPointUtil.doesItExist(adjData.get(Direction.UP))) - { - height = DataPointUtil.getHeight(singleAdjDataPoint); - depth = DataPointUtil.getDepth(singleAdjDataPoint); - }*/ - //Down direction case - singleAdjDataPoint = adjData.get(Direction.DOWN)[0]; - if(DataPointUtil.doesItExist(singleAdjDataPoint)) - skyLights.get(Direction.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - else - skyLights.get(Direction.DOWN)[0] = skyLights.get(Direction.UP)[0]; - //other sided - //TODO clean some similar cases - for (Direction direction : ADJ_DIRECTIONS) - { - if (isCulled(direction)) - continue; - - long[] dataPoint = adjData.get(direction); - if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0])) - { - adjHeight.get(direction)[0] = maxY; - adjDepth.get(direction)[0] = minY; - adjHeight.get(direction)[1] = VOID_FACE; - adjDepth.get(direction)[1] = VOID_FACE; - skyLights.get(direction)[0] = 15; //in void set full sky light - continue; - } - - int i; - int faceToDraw = 0; - boolean firstFace = true; - boolean toFinish = false; - int toFinishIndex = 0; - boolean allAbove = true; - for (i = 0; i < dataPoint.length; i++) - { - singleAdjDataPoint = dataPoint[i]; - - if (DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint)) - break; - - height = DataPointUtil.getHeight(singleAdjDataPoint); - depth = DataPointUtil.getDepth(singleAdjDataPoint); - - if (depth <= maxY) - { - allAbove = false; - if (height < minY) - { - // the adj data is lower than the current data - - if (firstFace) - { - adjHeight.get(direction)[0] = getMaxY(); - adjDepth.get(direction)[0] = getMinY(); - skyLights.get(direction)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0]; - } - else - { - adjDepth.get(direction)[faceToDraw] = getMinY(); - skyLights.get(direction)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - } - faceToDraw++; - toFinish = false; - - // break since all the other data will be lower - break; - } - else if (depth <= minY) - { - if (height >= maxY) - { - // the adj data is inside the current data - // don't draw the face - adjHeight.get(direction)[0] = VOID_FACE; - adjDepth.get(direction)[0] = VOID_FACE; - } - else // height < maxY - { - // the adj data intersects the lower part of the current data - // if this is the only face, use the maxY and break, - // if there was another face we finish the last one and break - if (firstFace) - { - adjHeight.get(direction)[0] = getMaxY(); - adjDepth.get(direction)[0] = height; - skyLights.get(direction)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0]; - } - else - { - adjDepth.get(direction)[faceToDraw] = height; - skyLights.get(direction)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - } - toFinish = false; - faceToDraw++; - } - break; - } - else if (height >= maxY)//depth > minY && - { - // the adj data intersects the higher part of the current data - // we start the creation of a new face - adjHeight.get(direction)[faceToDraw] = depth; - //skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - firstFace = false; - toFinish = true; - toFinishIndex = i + 1; - } - else - { - // if (depth > minY && height < maxY) - - // the adj data is contained in the current data - if (firstFace) - { - adjHeight.get(direction)[0] = getMaxY(); - } - - adjDepth.get(direction)[faceToDraw] = height; - skyLights.get(direction)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - faceToDraw++; - adjHeight.get(direction)[faceToDraw] = depth; - firstFace = false; - toFinish = true; - toFinishIndex = i + 1; - } - } - } - - if (allAbove) - { - adjHeight.get(direction)[0] = getMaxY(); - adjDepth.get(direction)[0] = getMinY(); - skyLights.get(direction)[0] = skyLights.get(Direction.UP)[0]; - faceToDraw++; - } - else if (toFinish) - { - adjDepth.get(direction)[faceToDraw] = minY; - if(toFinishIndex < dataPoint.length) - { - singleAdjDataPoint = dataPoint[toFinishIndex]; - if (DataPointUtil.doesItExist(singleAdjDataPoint)) - skyLights.get(direction)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - else - skyLights.get(direction)[faceToDraw] = skyLights.get(Direction.UP)[0]; - } - faceToDraw++; - } - - adjHeight.get(direction)[faceToDraw] = VOID_FACE; - adjDepth.get(direction)[faceToDraw] = VOID_FACE; - } - } - - /** We use this method to set the various width of the column */ - public void setWidth(int xWidth, int yWidth, int zWidth) - { - boxWidth[X] = xWidth; - boxWidth[Y] = yWidth; - boxWidth[Z] = zWidth; - } - - /** We use this method to set the various offset of the column */ - public void setOffset(int xOffset, int yOffset, int zOffset) - { - boxOffset[X] = xOffset; - boxOffset[Y] = yOffset; - boxOffset[Z] = zOffset; - } - - /** - * This method return the position of a face in the axis of the face - * This is useful for the face culling - * @param direction that we want to check - * @return position in the axis of the face - */ - public int getFacePos(Direction direction) - { - return boxOffset[FACE_DIRECTION.get(direction)[0]] + boxWidth[FACE_DIRECTION.get(direction)[0]] * FACE_DIRECTION.get(direction)[1]; - } - - /** - * returns true if the given direction should be rendered. - */ - public boolean shouldRenderFace(Direction direction, int adjIndex) - { - if (direction == Direction.UP || direction == Direction.DOWN) - return adjIndex == 0; - return !(adjHeight.get(direction)[adjIndex] == VOID_FACE && adjDepth.get(direction)[adjIndex] == VOID_FACE); - } - - - /** - * @param direction direction of the face we want to render - * @param vertexIndex index of the vertex of the face (0-1-2-3) - * @return position x of the relative vertex - */ - public int getX(Direction direction, int vertexIndex) - { - return boxOffset[X] + boxWidth[X] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][X]; - } - - /** - * @param direction direction of the face we want to render - * @param vertexIndex index of the vertex of the face (0-1-2-3) - * @return position y of the relative vertex - */ - public int getY(Direction direction, int vertexIndex) - { - return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y]; - } - - /** - * @param direction direction of the face we want to render - * @param vertexIndex index of the vertex of the face (0-1-2-3) - * @param adjIndex, index of the n-th culled face of this direction - * @return position x of the relative vertex - */ - public int getY(Direction direction, int vertexIndex, int adjIndex) - { - if (direction == Direction.DOWN || direction == Direction.UP) - return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y]; - else - { - // this could probably be cleaned up more, - // but it still works - if (1 - DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y] == ADJACENT_HEIGHT_INDEX) - return adjHeight.get(direction)[adjIndex]; - else - return adjDepth.get(direction)[adjIndex]; - } - } - - /** - * @param direction direction of the face we want to render - * @param vertexIndex index of the vertex of the face (0-1-2-3) - * @return position z of the relative vertex - */ - public int getZ(Direction direction, int vertexIndex) - { - return boxOffset[Z] + boxWidth[Z] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Z]; - } - - public int getMinX() - { - return boxOffset[X]; - } - - public int getMaxX() - { - return boxOffset[X] + boxWidth[X]; - } - - public int getMinY() - { - return boxOffset[Y]; - } - - public int getMaxY() - { - return boxOffset[Y] + boxWidth[Y]; - } - - public int getMinZ() - { - return boxOffset[Z]; - } - - public int getMaxZ() - { - return boxOffset[Z] + boxWidth[Z]; - } - -} diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java deleted file mode 100644 index f1045bcf5..000000000 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.bufferBuilding.lodTemplates; - -import java.util.Map; - -import com.seibel.lod.enums.DebugMode; -import com.seibel.lod.util.ColorUtil; -import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; - - -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.util.Direction; - -/** - * Builds LODs as rectangular prisms. - * @author James Seibel - * @version 10-10-2021 - */ -public class CubicLodTemplate extends AbstractLodTemplate -{ - - public CubicLodTemplate() - { - - } - - @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map adjData, - byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled) - { - if (box == null) - return; - - // equivalent to 2^detailLevel - int blockWidth = 1 << detailLevel; - - int color; - if (debugging != DebugMode.OFF) - color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB(); - else - color = DataPointUtil.getColor(data); - - - generateBoundingBox( - box, - DataPointUtil.getHeight(data), - DataPointUtil.getDepth(data), - blockWidth, - posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset - bufferCenterBlockPos, - adjData, - color, - DataPointUtil.getLightSkyAlt(data), - DataPointUtil.getLightBlock(data), - adjShadeDisabled); - - addBoundingBoxToBuffer(buffer, box); - } - - private void generateBoundingBox(Box box, - int height, int depth, int width, - double xOffset, double yOffset, double zOffset, - BlockPosWrapper bufferCenterBlockPos, - Map adjData, - int color, - int skyLight, - int blockLight, - boolean[] adjShadeDisabled) - { - // don't add an LOD if it is empty - if (height == -1 && depth == -1) - return; - - if (depth == height) - // if the top and bottom points are at the same height - // render this LOD as 1 block thick - height++; - - // offset the AABB by its x/z position in the world since - // it uses doubles to specify its location, unlike the model view matrix - // which only uses floats - double x = -bufferCenterBlockPos.getX(); - double z = -bufferCenterBlockPos.getZ(); - box.reset(); - box.setColor(color, adjShadeDisabled); - box.setLights(skyLight, blockLight); - box.setWidth(width, height - depth, width); - box.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z)); - box.setUpCulling(32, bufferCenterBlockPos); - box.setAdjData(adjData); - } - - private void addBoundingBoxToBuffer(BufferBuilder buffer, Box box) - { - int color; - int skyLight; - int blockLight; - for (Direction direction : Box.DIRECTIONS) - { - if(box.isCulled(direction)) - continue; - int verticalFaceIndex = 0; - while (box.shouldRenderFace(direction, verticalFaceIndex)) - { - for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++) - { - color = box.getColor(direction); - skyLight = box.getSkyLight(direction, verticalFaceIndex); - blockLight = box.getBlockLight(); - color = ColorUtil.applyLightValue(color, skyLight, blockLight); - addPosAndColor(buffer, - box.getX(direction, vertexIndex), - box.getY(direction, vertexIndex, verticalFaceIndex), - box.getZ(direction, vertexIndex), - color); - } - verticalFaceIndex++; - } - } - } - -} diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java deleted file mode 100644 index e3f92f067..000000000 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.bufferBuilding.lodTemplates; - -import java.util.Map; - -import com.seibel.lod.enums.DebugMode; -import com.seibel.lod.proxy.ClientProxy; - -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.util.Direction; - -/** - * TODO DynamicLodTemplate - * Chunks smoothly transition between - * each other, unless a neighboring chunk - * is at a significantly different height. - * @author James Seibel - * @version 06-16-2021 - */ -public class DynamicLodTemplate extends AbstractLodTemplate -{ - @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map adjData, - byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled) - { - ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!"); - } - -} diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java deleted file mode 100644 index 2bd3491e1..000000000 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.bufferBuilding.lodTemplates; - -import java.util.Map; - -import com.seibel.lod.enums.DebugMode; -import com.seibel.lod.proxy.ClientProxy; - -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.util.Direction; - -/** - * TODO #21 TriangularLodTemplate - * Builds each LOD chunk as a singular rectangular prism. - * @author James Seibel - * @version 06-16-2021 - */ -public class TriangularLodTemplate extends AbstractLodTemplate -{ - @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map adjData, - byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled) - { - ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!"); - } - -} diff --git a/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java b/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java deleted file mode 100644 index a76b768ae..000000000 --- a/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java +++ /dev/null @@ -1,535 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.lodBuilding; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.enums.HorizontalResolution; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.LodRegion; -import com.seibel.lod.objects.LodWorld; -import com.seibel.lod.util.ColorUtil; -import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.DetailDistanceUtil; -import com.seibel.lod.util.LevelPosUtil; -import com.seibel.lod.util.LodThreadFactory; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.util.ThreadMapUtil; -import com.seibel.lod.wrappers.MinecraftWrapper; -import com.seibel.lod.wrappers.Block.BlockColorWrapper; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import com.seibel.lod.wrappers.Block.BlockShapeWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkWrapper; -import com.seibel.lod.wrappers.World.BiomeWrapper; -import com.seibel.lod.wrappers.World.LevelWrapper; - -import net.minecraft.world.DimensionType; -import net.minecraft.world.IWorld; - -/** - * This object is in charge of creating Lod related objects. - * - * @author Cola - * @author Leonardo Amato - * @author James Seibel - * @version 10-22-2021 - */ -public class LodBuilder -{ - private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; - - private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); - - - /** If no blocks are found in the area in determineBottomPointForArea return this */ - public static final short DEFAULT_DEPTH = 0; - /** If no blocks are found in the area in determineHeightPointForArea return this */ - public static final short DEFAULT_HEIGHT = 0; - /** Minecraft's max light value */ - public static final short DEFAULT_MAX_LIGHT = 15; - - - /** - * How wide LodDimensions should be in regions
- * Is automatically set before the first frame in ClientProxy. - */ - public int defaultDimensionWidthInRegions = 0; - - //public static final boolean useExperimentalLighting = true; - - - - - public LodBuilder() - { - - } - - public void generateLodNodeAsync(ChunkWrapper chunk, LodWorld lodWorld, IWorld world) - { - generateLodNodeAsync(chunk, lodWorld, world, DistanceGenerationMode.SERVER); - } - - public void generateLodNodeAsync(ChunkWrapper chunk, LodWorld lodWorld, IWorld world, DistanceGenerationMode generationMode) - { - if (lodWorld == null || lodWorld.getIsWorldNotLoaded()) - return; - - // don't try to create an LOD object - // if for some reason we aren't - // given a valid chunk object - if (chunk == null) - return; - - Thread thread = new Thread(() -> - { - try - { - // we need a loaded client world in order to - // get the textures for blocks - if (mc.getClientLevel() == null) - return; - - // don't try to generate LODs if the user isn't in the world anymore - // (this happens a lot when the user leaves a world/server) - if (mc.getSinglePlayerServer() == null && mc.getCurrentServer() == null) - return; - - DimensionType dim = world.dimensionType(); - - // make sure the dimension exists - LodDimension lodDim; - if (lodWorld.getLodDimension(dim) == null) - { - lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions); - lodWorld.addLodDimension(lodDim); - } - else - { - lodDim = lodWorld.getLodDimension(dim); - } - generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode)); - } - catch (IllegalArgumentException | NullPointerException e) - { - e.printStackTrace(); - // if the world changes while LODs are being generated - // they will throw errors as they try to access things that no longer - // exist. - } - }); - lodGenThreadPool.execute(thread); - } - - /** - * Creates a LodNode for a chunk in the given world. - * @throws IllegalArgumentException thrown if either the chunk or world is null. - */ - public void generateLodNodeFromChunk(LodDimension lodDim, ChunkWrapper chunk) throws IllegalArgumentException - { - generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig()); - } - - /** - * Creates a LodNode for a chunk in the given world. - * @throws IllegalArgumentException thrown if either the chunk or world is null. - */ - public void generateLodNodeFromChunk(LodDimension lodDim, ChunkWrapper chunk, LodBuilderConfig config) - throws IllegalArgumentException - { - if (chunk == null) - throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); - - int startX; - int startZ; - - - LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ()); - if (region == null) - return; - - // this happens if a LOD is generated after the user leaves the world. - if (MinecraftWrapper.INSTANCE.getWrappedClientLevel() == null) - return; - - // determine how many LODs to generate horizontally - byte minDetailLevel = region.getMinDetailLevel(); - HorizontalResolution detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel); - - - // determine how many LODs to generate vertically - //VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get(); - byte detailLevel = detail.detailLevel; - - - // generate the LODs - int posX; - int posZ; - for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++) - { - startX = detail.startX[i]; - startZ = detail.startZ[i]; - - long[] data; - long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ); - data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel)); - - - //lodDim.clear(detailLevel, posX, posZ); - if (data != null && data.length != 0) - { - posX = LevelPosUtil.convert((byte) 0, chunk.getPos().getX() * 16 + startX, detail.detailLevel); - posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().getZ() * 16 + startZ, detail.detailLevel); - lodDim.addVerticalData(detailLevel, posX, posZ, data, false); - } - } - lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().getX(), chunk.getPos().getZ()); - } - - /** creates a vertical DataPoint */ - private long[] createVerticalDataToMerge(HorizontalResolution detail, ChunkWrapper chunk, LodBuilderConfig config, int startX, int startZ) - { - // equivalent to 2^detailLevel - int size = 1 << detail.detailLevel; - - long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel); - int verticalData = DataPointUtil.worldHeight / 2 + 1; - - ChunkPosWrapper chunkPos = chunk.getPos(); - int height; - int depth; - int color; - int light; - int lightSky; - int lightBlock; - int generation = config.distanceGenerationMode.complexity; - - int xRel; - int zRel; - int xAbs; - int yAbs; - int zAbs; - boolean hasCeiling = mc.getClientLevel().dimensionType().hasCeiling(); - boolean hasSkyLight = mc.getClientLevel().dimensionType().hasSkyLight(); - boolean isDefault; - BlockPosWrapper blockPos = new BlockPosWrapper(); - int index; - - for (index = 0; index < size * size; index++) - { - xRel = startX + index % size; - zRel = startZ + index / size; - xAbs = chunkPos.getMinBlockX() + xRel; - zAbs = chunkPos.getMinBlockZ() + zRel; - - //Calculate the height of the lod - yAbs = DataPointUtil.worldHeight + 1; - int count = 0; - boolean topBlock = true; - while (yAbs > 0) - { - height = determineHeightPointFrom(chunk, config, xRel, yAbs, zRel, blockPos); - - // If the lod is at the default height, it must be void data - if (height == DEFAULT_HEIGHT) - { - if (topBlock) - dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation); - break; - } - - yAbs = height - 1; - // We search light on above air block - depth = determineBottomPointFrom(chunk, config, xRel, yAbs, zRel, blockPos); - if (hasCeiling && topBlock) - { - yAbs = depth; - blockPos.set(xAbs, yAbs, zAbs); - light = getLightValue(chunk, blockPos, true, hasSkyLight, true); - color = generateLodColor(chunk, config, xAbs, yAbs, zAbs, blockPos); - blockPos.set(xAbs, yAbs - 1, zAbs); - } - else - { - blockPos.set(xAbs, yAbs, zAbs); - light = getLightValue(chunk, blockPos, hasCeiling, hasSkyLight, topBlock); - color = generateLodColor(chunk, config, xRel, yAbs, zRel, blockPos); - blockPos.set(xAbs, yAbs + 1, zAbs); - } - lightBlock = light & 0b1111; - lightSky = (light >> 4) & 0b1111; - isDefault = ((light >> 8)) == 1; - - dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height, depth, color, lightSky, lightBlock, generation, isDefault); - topBlock = false; - yAbs = depth - 1; - count++; - } - } - return dataToMerge; - } - - /** - * Find the lowest valid point from the bottom. - * Used when creating a vertical LOD. - */ - private short determineBottomPointFrom(ChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, BlockPosWrapper blockPos) - { - short depth = DEFAULT_DEPTH; - - for (int y = yAbs; y >= 0; y--) - { - blockPos.set(xAbs, y, zAbs); - if (!isLayerValidLodPoint(chunk, blockPos)) - { - depth = (short) (y + 1); - break; - } - } - return depth; - } - - /** Find the highest valid point from the Top */ - private short determineHeightPointFrom(ChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, BlockPosWrapper blockPos) - { - short height = DEFAULT_HEIGHT; - if (config.useHeightmap) - height = (short) chunk.getHeightMapValue(xAbs, zAbs); - else - { - for (int y = yAbs; y >= 0; y--) - { - blockPos.set(xAbs, y, zAbs); - if (isLayerValidLodPoint(chunk, blockPos)) - { - height = (short) (y + 1); - break; - } - } - } - return height; - } - - - - // =====================// - // constructor helpers // - // =====================// - - /** - * Generate the color for the given chunk using biome water color, foliage - * color, and grass color. - */ - private int generateLodColor(ChunkWrapper chunk, LodBuilderConfig config, int xRel, int yAbs, int zRel, BlockPosWrapper blockPos) - { - int colorInt; - if (config.useBiomeColors) - { - // I have no idea why I need to bit shift to the right, but - // if I don't the biomes don't show up correctly. - colorInt = chunk.getBiome(xRel, yAbs, zRel).getColorForBiome(xRel, zRel); - } - else - { - blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs, chunk.getPos().getMinBlockZ() + zRel); - colorInt = getColorForBlock(chunk, blockPos); - - // if we are skipping non-full and non-solid blocks that means we ignore - // snow, flowers, etc. Get the above block so we can still get the color - // of the snow, flower, etc. that may be above this block - int aboveColorInt = 0; - if (LodConfig.CLIENT.worldGenerator.blockToAvoid.get().nonFull || LodConfig.CLIENT.worldGenerator.blockToAvoid.get().noCollision) - { - blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs + 1, chunk.getPos().getMinBlockZ() + zRel); - aboveColorInt = getColorForBlock(chunk, blockPos); - } - - //if (colorInt == 0 && yAbs > 0) - // if this block is invisible, check the block below it - // colorInt = generateLodColor(chunk, config, xRel, yAbs - 1, zRel, blockPos); - - // override this block's color if there was a block above this - // and we were avoiding non-full/non-solid blocks - if (aboveColorInt != 0) - colorInt = aboveColorInt; - } - - return colorInt; - } - - /** Gets the light value for the given block position */ - private int getLightValue(ChunkWrapper chunk, BlockPosWrapper blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock) - { - int skyLight = 0; - int blockLight; - // 1 means the lighting is a guess - int isDefault = 0; - - LevelWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerLevel(); - - int blockBrightness = chunk.getEmittedBrightness(blockPos); - // get the air block above or below this block - if (hasCeiling && topBlock) - blockPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ()); - else - blockPos.set(blockPos.getX(), blockPos.getY() + 1, blockPos.getZ()); - - - - if (world != null && !world.isEmpty()) - { - // server world sky light (always accurate) - blockLight = world.getBlockLight(blockPos); - if (topBlock && !hasCeiling && hasSkyLight) - skyLight = DEFAULT_MAX_LIGHT; - else - { - if (hasSkyLight) - skyLight = world.getSkyLight(blockPos); - //else - // skyLight = 0; - } - if (!topBlock && skyLight == 15) - { - // we are on predicted terrain, and we don't know what the light here is, - // lets just take a guess - if (blockPos.getY() >= mc.getClientLevel().getSeaLevel() - 5) - { - skyLight = 12; - isDefault = 1; - } - else - skyLight = 0; - } - } - else - { - world = MinecraftWrapper.INSTANCE.getWrappedClientLevel(); - if (world.isEmpty()) - return 0; - // client world sky light (almost never accurate) - blockLight = world.getBlockLight(blockPos); - // estimate what the lighting should be - if (hasSkyLight || !hasCeiling) - { - if (topBlock) - skyLight = DEFAULT_MAX_LIGHT; - else - { - - if (hasSkyLight) - skyLight = world.getSkyLight(blockPos); - //else - // skyLight = 0; - - if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15)) - { - // we don't know what the light here is, - // lets just take a guess - if (blockPos.getY() >= mc.getClientLevel().getSeaLevel() - 5) - { - skyLight = 12; - isDefault = 1; - } - else - skyLight = 0; - } - } - if (hasSkyLight) - skyLight = 0; - } - } - - blockLight = LodUtil.clamp(0, Math.max(blockLight, blockBrightness), DEFAULT_MAX_LIGHT); - - return blockLight + (skyLight << 4) + (isDefault << 8); - } - - /** Returns a color int for the given block. */ - private int getColorForBlock(ChunkWrapper chunk, BlockPosWrapper blockPos) - { - - - int colorOfBlock; - int colorInt; - - int xRel = blockPos.getX() - chunk.getPos().getMinBlockX(); - int zRel = blockPos.getZ() - chunk.getPos().getMinBlockZ(); - //int x = blockPos.getX(); - int y = blockPos.getY(); - //int z = blockPos.getZ(); - - BlockColorWrapper blockColorWrapper; - BlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos); - - if (chunk.isWaterLogged(blockPos)) - blockColorWrapper = BlockColorWrapper.getWaterColor(); - else - blockColorWrapper = chunk.getBlockColorWrapper(blockPos); - - if (blockShapeWrapper.isToAvoid()) - return 0; - - colorOfBlock = blockColorWrapper.getColor(); - - - if (blockColorWrapper.hasTint()) - { - BiomeWrapper biome = chunk.getBiome(xRel, y, zRel); - int tintValue; - if (blockColorWrapper.hasGrassTint()) - // grass and green plants - tintValue = biome.getGrassTint(0,0); - else if (blockColorWrapper.hasFolliageTint()) - tintValue = biome.getFolliageTint(); - else - //we can reintroduce this with the wrappers - tintValue = biome.getWaterTint(); - - colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, colorOfBlock); - } - else - colorInt = colorOfBlock; - return colorInt; - } - - - /** Is the block at the given blockPos a valid LOD point? */ - private boolean isLayerValidLodPoint(ChunkWrapper chunk, BlockPosWrapper blockPos) - { - - - if (chunk.isWaterLogged(blockPos)) - return true; - - boolean nonFullAvoidance = LodConfig.CLIENT.worldGenerator.blockToAvoid.get().nonFull; - boolean noCollisionAvoidance = LodConfig.CLIENT.worldGenerator.blockToAvoid.get().noCollision; - - BlockShapeWrapper block = chunk.getBlockShapeWrapper(blockPos); - return !block.isToAvoid() - && !(nonFullAvoidance && block.isNonFull()) - && !(noCollisionAvoidance && block.hasNoCollision()); - - } -} diff --git a/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilderConfig.java b/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilderConfig.java deleted file mode 100644 index 89569f486..000000000 --- a/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilderConfig.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.lodBuilding; - -import com.seibel.lod.enums.DistanceGenerationMode; - -/** - * This is used to easily configure how LodChunks are generated. - * Generally this will only be used if we want to generate a - * LodChunk using an incomplete Chunk, otherwise the defaults - * work best for a fully generated chunk (IE has correct surface blocks). - * @author James Seibel - * @version 8-14-2021 - */ -public class LodBuilderConfig -{ - /** default: false */ - public boolean useHeightmap; - /** default: false */ - public boolean useBiomeColors; - /** default: true */ - public boolean useSolidBlocksInColorGen; - /** default: server */ - public DistanceGenerationMode distanceGenerationMode; - - /** - * default settings for a normal chunk
- * useHeightmap = false
- * useBiomeColors = false
- * useSolidBlocksInColorGen = true
- * generationMode = Server
- */ - public LodBuilderConfig() - { - useHeightmap = false; - useBiomeColors = false; - useSolidBlocksInColorGen = true; - distanceGenerationMode = DistanceGenerationMode.SERVER; - } - - /** - * @param newUseHeightmap default = false - * @param newUseBiomeColors default = false - * @param newUseSolidBlocksInBiomeColor default = true - * @param newDistanceGenerationMode default = Server - */ - public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, - boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode) - { - useHeightmap = newUseHeightmap; - useBiomeColors = newUseBiomeColors; - useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor; - distanceGenerationMode = newDistanceGenerationMode; - } - - /** - * @param newUseHeightmap default = false - * @param newUseBiomeColors default = false - * @param newUseSolidBlocksInBiomeColor default = true - */ - public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor) - { - this(); - useHeightmap = newUseHeightmap; - useBiomeColors = newUseBiomeColors; - useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor; - distanceGenerationMode = newUseHeightmap ? DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT : DistanceGenerationMode.BIOME_ONLY; - } - - /** - * @param newDistanceGenerationMode default = Server - */ - public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode) - { - this(); - distanceGenerationMode = newDistanceGenerationMode; - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodGenWorker.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodGenWorker.java deleted file mode 100644 index 63ce0c0df..000000000 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodGenWorker.java +++ /dev/null @@ -1,705 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.worldGeneration; - -import java.util.ConcurrentModificationException; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Supplier; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.seibel.lod.builders.lodBuilding.LodBuilder; -import com.seibel.lod.builders.lodBuilding.LodBuilderConfig; -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.proxy.ClientProxy; -import com.seibel.lod.util.LodUtil; - -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkWrapper; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.util.palette.UpgradeData; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.chunk.ChunkPrimer; -import net.minecraft.world.chunk.ChunkStatus; -import net.minecraft.world.chunk.IChunk; -import net.minecraft.world.gen.ChunkGenerator; -import net.minecraft.world.gen.Heightmap; -import net.minecraft.world.gen.blockstateprovider.WeightedBlockStateProvider; -import net.minecraft.world.gen.feature.BlockClusterFeatureConfig; -import net.minecraft.world.gen.feature.ConfiguredFeature; -import net.minecraft.world.gen.feature.DecoratedFeatureConfig; -import net.minecraft.world.gen.feature.FeatureSpread; -import net.minecraft.world.gen.feature.FeatureSpreadConfig; -import net.minecraft.world.gen.feature.IceAndSnowFeature; -import net.minecraft.world.gen.feature.NoFeatureConfig; -import net.minecraft.world.gen.feature.template.TemplateManager; -import net.minecraft.world.gen.placement.ConfiguredPlacement; -import net.minecraft.world.gen.placement.DecoratedPlacementConfig; -import net.minecraft.world.gen.placement.IPlacementConfig; -import net.minecraft.world.gen.placement.NoiseDependant; -import net.minecraft.world.server.ServerChunkProvider; -import net.minecraft.world.server.ServerWorld; -import net.minecraft.world.server.ServerWorldLightManager; -import net.minecraftforge.common.WorldWorkerManager.IWorker; - -/** - * This is used to generate a LodChunk at a given ChunkPos. - * - * @author James Seibel - * @version 10-22-2021 - */ -public class LodGenWorker implements IWorker -{ - public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.get(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); - - private boolean threadStarted = false; - private final LodChunkGenThread thread; - - - /** - * If a configured feature fails for whatever reason, - * add it to this list, this is to hopefully remove any - * features that could cause issues down the line. - */ - private static final ConcurrentHashMap> configuredFeaturesToAvoid = new ConcurrentHashMap<>(); - - - - public LodGenWorker(ChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode, - LodBuilder newLodBuilder, - LodDimension newLodDimension, ServerWorld newServerWorld) - { - // just a few sanity checks - if (newPos == null) - throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos"); - - if (newLodBuilder == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder"); - - if (newLodDimension == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension"); - - if (newServerWorld == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld"); - - - - thread = new LodChunkGenThread(newPos, newGenerationMode, - newLodBuilder, - newLodDimension, newServerWorld); - } - - @Override - public boolean doWork() - { - if (!threadStarted) - { - if (LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get() == DistanceGenerationMode.SERVER) - { - // if we are using SERVER generation that has to be done - // synchronously to prevent crashing and harmful - // interactions with the normal world generator - thread.run(); - } - else - { - // Every other method can - // be done asynchronously - Thread newThread = new Thread(thread); - newThread.setPriority(5); - genThreads.execute(newThread); - } - - threadStarted = true; - - // useful for debugging -// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods()); -// ClientProxy.LOGGER.info(genThreads.toString()); - } - - return false; - } - - @Override - public boolean hasWork() - { - return !threadStarted; - } - - - - - private static class LodChunkGenThread implements Runnable - { - public final ServerWorld serverWorld; - public final LodDimension lodDim; - public final DistanceGenerationMode generationMode; - public final LodBuilder lodBuilder; - - private final ChunkPosWrapper pos; - - public LodChunkGenThread(ChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode, - LodBuilder newLodBuilder, - LodDimension newLodDimension, ServerWorld newServerWorld) - { - pos = newPos; - generationMode = newGenerationMode; - lodBuilder = newLodBuilder; - lodDim = newLodDimension; - serverWorld = newServerWorld; - } - - @Override - public void run() - { - //try - //{ - // only generate LodChunks if they can - // be added to the current LodDimension - - /* TODO I must disable this 'if', if I will find a way to replace it */ - if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS)) - { - // - //{ - // lodBuilder.generateLodNodeFromChunk(lodDim, loadedChunk, new LodBuilderConfig(DistanceGenerationMode.SERVER)); - //} - //else - //{ - /* - IChunk loadedChunk = null; - if (lodDim.isChunkPreGenerated(pos.x, pos.z) && LodConfig.CLIENT.worldGenerator.useExperimentalPreGenLoading.get()) - { - // generate a Lod like normal - loadedChunk = ChunkLoader.getChunkFromFile(pos); - if(loadedChunk != null) - lodBuilder.generateLodNodeFromChunk(lodDim, loadedChunk, new LodBuilderConfig(DistanceGenerationMode.SERVER)); - else - { - switch (generationMode) - { - case NONE: - // don't generate - break; - case BIOME_ONLY: - case BIOME_ONLY_SIMULATE_HEIGHT: - // fastest - generateUsingBiomesOnly(); - break; - case SURFACE: - // faster - generateUsingSurface(); - break; - case FEATURES: - // fast - generateUsingFeatures(); - break; - case SERVER: - // very slow - lodBuilder.generateLodNodeFromChunk(lodDim, serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), new LodBuilderConfig(DistanceGenerationMode.SERVER)); - //generateWithServer(); - break; - } - } - } - else - {*/ - switch (generationMode) - { - case NONE: - // don't generate - break; - case BIOME_ONLY: - case BIOME_ONLY_SIMULATE_HEIGHT: - // fastest - generateUsingBiomesOnly(); - break; - case SURFACE: - // faster - generateUsingSurface(); - break; - case FEATURES: - // fast - generateUsingFeatures(); - break; - case SERVER: - // very slow - generateWithServer(); - break; - } - //} - - //lodRenderer.regenerateLODsNextFrame(); - -/* - boolean dataExistence = lodDim.doesDataExist(new LevelPos((byte) 3, pos.x, pos.z)); - if (dataExistence) - ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!"); - else - ClientProxy.LOGGER.info(pos.x + " " + pos.z); - */ - // shows the pool size, active threads, queued tasks and completed tasks -// ClientProxy.LOGGER.info(genThreads.toString()); - -// long endTime = System.currentTimeMillis(); -// System.out.println(endTime - startTime); - - }// if in range - //} - //catch (Exception e) - //{ - // ClientProxy.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage()); - // e.printStackTrace(); - //} - //finally - //{ - // decrement how many threads are running - LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1); - - // this position is no longer being generated - LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos); - //} - - }// run - - - - /** - * takes about 2-5 ms - */ - private void generateUsingBiomesOnly() - { - List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY); - chunkList.add(chunk); - - ServerChunkProvider chunkSource = serverWorld.getChunkSource(); - ChunkGenerator chunkGen = chunkSource.generator; - - // generate the terrain (this is thread safe) - ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); - // override the chunk status, so we can run the next generator stage - chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); - chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); - chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); - - - - - // generate fake height data for this LOD - int seaLevel = serverWorld.getSeaLevel(); - - boolean simulateHeight = generationMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; - boolean inTheEnd = false; - - // add fake heightmap data so our LODs aren't at height 0 - Heightmap heightmap = new Heightmap(chunk, LodUtil.DEFAULT_HEIGHTMAP); - for (int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++) - { - for (int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++) - { - if (simulateHeight) - { - // these heights are of course aren't super accurate, - // they are just to simulate height data where there isn't any - switch (chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory()) - { - case NETHER: - heightmap.setHeight(x, z, serverWorld.getHeight() / 2); - break; - - case EXTREME_HILLS: - heightmap.setHeight(x, z, seaLevel + 30); - break; - case MESA: - case JUNGLE: - heightmap.setHeight(x, z, seaLevel + 20); - break; - case BEACH: - heightmap.setHeight(x, z, seaLevel + 5); - break; - case NONE: - heightmap.setHeight(x, z, 0); - break; - - case OCEAN: - case RIVER: - heightmap.setHeight(x, z, seaLevel); - break; - - case THEEND: - inTheEnd = true; - break; - - // DESERT - // FOREST - // ICY - // MUSHROOM - // SAVANNA - // SWAMP - // TAIGA - // PLAINS - default: - heightmap.setHeight(x, z, seaLevel + 10); - break; - }// heightmap switch - } - else - { - // we aren't simulating height - // always use sea level - heightmap.setHeight(x, z, seaLevel); - } - }// z - }// x - - chunk.setHeightmap(LodUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData()); - - - if (!inTheEnd) - { - lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false)); - } - else - { - // if we are in the end, don't generate any chunks. - // Since we don't know where the islands are, everything - // generates the same, and it looks awful. - //TODO it appears that 'if' can be collapsed, but comment says that it should not be a case - lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false)); - } - - -// long startTime = System.currentTimeMillis(); -// long endTime = System.currentTimeMillis(); -// System.out.println(endTime - startTime); - } - - - /** - * takes about 10 - 20 ms - */ - private void generateUsingSurface() - { - List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY); - chunkList.add(chunk); - LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); - - ServerChunkProvider chunkSource = serverWorld.getChunkSource(); - ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine(); - TemplateManager templateManager = serverWorld.getStructureManager(); - ChunkGenerator chunkGen = chunkSource.generator; - - - // generate the terrain (this is thread safe) - ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); - // override the chunk status, so we can run the next generator stage - chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); - chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); - ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); - ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); - - // this feature has been proven to be thread safe, - // so we will add it - IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC); - snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null); - - - lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.SURFACE)); - - /*TODO if we want to use Biome utils and terrain utils for overworld - * lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/ - } - - - /** - * takes about 15 - 20 ms - *

- * Causes concurrentModification Exceptions, - * which could cause instability or world generation bugs - */ - private void generateUsingFeatures() - { - List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY); - chunkList.add(chunk); - LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); - - ServerChunkProvider chunkSource = serverWorld.getChunkSource(); - ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine(); - TemplateManager templateManager = serverWorld.getStructureManager(); - ChunkGenerator chunkGen = chunkSource.generator; - - - // generate the terrain (this is thread safe) - ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); - // override the chunk status, so we can run the next generator stage - chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); - chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); - ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); - ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); - - - // get all the biomes in the chunk - HashSet biomes = new HashSet<>(); - for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) - { - for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) - { - Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2); - - // Issue #35 - // For some reason Jungle biomes cause incredible lag - // the features here must be interacting with each other - // in unpredictable ways (specifically tree feature generation). - // When generating Features my CPU usage generally hovers around 30 - 40% - // when generating Jungles it spikes to 100%. - if (biome.getBiomeCategory() != Biome.Category.JUNGLE) - { - // should probably use the heightmap here instead of seaLevel, - // but this seems to get the job done well enough - biomes.add(biome); - } - } - } - - boolean allowUnstableFeatures = LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.get(); - - // generate all the features related to this chunk. - // this may or may not be thread safe - for (Biome biome : biomes) - { - List>>> featuresForState = biome.generationSettings.features(); - - for (List>> suppliers : featuresForState) - { - for (Supplier> featureSupplier : suppliers) - { - ConfiguredFeature configuredFeature = featureSupplier.get(); - - if (!allowUnstableFeatures && - configuredFeaturesToAvoid.containsKey(configuredFeature.hashCode())) - continue; - - - try - { - configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition()); - } - catch (ConcurrentModificationException | UnsupportedOperationException e) - { - // This will happen. I'm not sure what to do about it - // except pray that it doesn't affect the normal world generation - // in any harmful way. - // Update: this can cause crashes and high CPU usage. - - // Issue #35 - // I tried cloning the config for each feature, but that - // path was blocked since I can't clone lambda methods. - // I tried using a deep cloning library and discovered - // the problem there. - // ( https://github.com/kostaskougios/cloning - // and - // https://github.com/EsotericSoftware/kryo ) - - if (!allowUnstableFeatures) - configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature); -// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); - } - // This will happen when the LodServerWorld - // isn't able to return something that a feature - // generator needs - catch (Exception e) - { - // I'm not sure what happened, print to the log - - e.printStackTrace(); - - if (!allowUnstableFeatures) - configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature); -// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); - } - } - } - } - - // generate a Lod like normal - lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.FEATURES)); - } - - - /** - * on pre generated chunks 0 - 1 ms - * on un generated chunks 0 - 50 ms - * with the median seeming to hover around 15 - 30 ms - * and outliers in the 100 - 200 ms range - *

- * Note this should not be multithreaded and does cause server/simulation lag - * (Higher lag for generating than loading) - */ - private void generateWithServer() - { - lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.SERVER)); - } - - - - - - - //================// - // Unused methods // - //================// - - // Sadly I wasn't able to get these to work, - // they are here for documentation purposes - - @SuppressWarnings({ "rawtypes", "unchecked", "unused" }) - private DecoratedFeatureConfig cloneDecoratedFeatureConfig(DecoratedFeatureConfig config) - { - IPlacementConfig placementConfig; - - Class oldConfigClass = config.decorator.config().getClass(); - - if (oldConfigClass == FeatureSpreadConfig.class) - { - FeatureSpreadConfig oldPlacementConfig = (FeatureSpreadConfig) config.decorator.config(); - FeatureSpread oldSpread = oldPlacementConfig.count(); - - placementConfig = new FeatureSpreadConfig(oldSpread); - } - else if (oldConfigClass == DecoratedPlacementConfig.class) - { - DecoratedPlacementConfig oldPlacementConfig = (DecoratedPlacementConfig) config.decorator.config(); - placementConfig = new DecoratedPlacementConfig(oldPlacementConfig.inner(), oldPlacementConfig.outer()); - } - else if (oldConfigClass == NoiseDependant.class) - { - NoiseDependant oldPlacementConfig = (NoiseDependant) config.decorator.config(); - placementConfig = new NoiseDependant(oldPlacementConfig.noiseLevel, oldPlacementConfig.belowNoise, oldPlacementConfig.aboveNoise); - } - else - { -// ClientProxy.LOGGER.debug("unknown decorated placement config: \"" + config.decorator.config().getClass() + "\""); - return config; - } - - - ConfiguredPlacement newPlacement = new ConfiguredPlacement(config.decorator.decorator, placementConfig); - return new DecoratedFeatureConfig(config.feature, newPlacement); - } - - - @SuppressWarnings("unused") - private BlockClusterFeatureConfig cloneBlockClusterFeatureConfig(BlockClusterFeatureConfig config) - { - WeightedBlockStateProvider provider = new WeightedBlockStateProvider(); - provider.weightedList.entries.addAll(((WeightedBlockStateProvider) config.stateProvider).weightedList.entries); - - HashSet whitelist = new HashSet<>(config.whitelist); - - HashSet blacklist = new HashSet<>(config.blacklist); - - - BlockClusterFeatureConfig.Builder builder = new BlockClusterFeatureConfig.Builder(provider, config.blockPlacer); - builder.whitelist(whitelist); - builder.blacklist(blacklist); - builder.xspread(config.xspread); - builder.yspread(config.yspread); - builder.zspread(config.zspread); - if (config.canReplace) - { - builder.canReplace(); - } - if (config.needWater) - { - builder.needWater(); - } - if (config.project) - { - builder.noProjection(); - } - builder.tries(config.tries); - - - return builder.build(); - } - - } - - - /** - * Stops the current genThreads if they are running - * and then recreates the Executor service.

- *

- * This is done to clear any outstanding tasks - * that may exist after the player leaves their current world. - * If this isn't done unfinished tasks may be left in the queue - * preventing new LodChunks form being generated. - */ - public static void restartExecutorService() - { - if (genThreads != null && !genThreads.isShutdown()) - { - genThreads.shutdownNow(); - } - genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.get(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); - } - - - - - - /* - * performance/generation tests related to - * serverWorld.getChunk(x, z, ChunkStatus. *** ) - - true/false is whether they generated blocks or not - the time is how long it took to generate - - ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P) - ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks) - ChunkStatus.BIOMES 1 - 10 ms false (no height) - ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone) - ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass) - ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass) - ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass) - ChunkStatus.FEATURES 7 - 25 ms true - ChunkStatus.HEIGHTMAPS 20 - 40 ms true - ChunkStatus.LIGHT 20 - 40 ms true - ChunkStatus.FULL 30 - 50 ms true - ChunkStatus.SPAWN 50 - 80 ms true - - - At this point I would suggest using FEATURES, as it generates snow and trees - (and any other object that is needed to make biomes distinct) - - Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much) - */ -} diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java deleted file mode 100644 index b4faaca6d..000000000 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.builders.worldGeneration; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; - -import com.seibel.lod.builders.lodBuilding.LodBuilder; -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.PosToGenerateContainer; -import com.seibel.lod.render.LodRenderer; -import com.seibel.lod.util.DetailDistanceUtil; -import com.seibel.lod.util.LevelPosUtil; -import com.seibel.lod.util.LodThreadFactory; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; -import com.seibel.lod.wrappers.MinecraftWrapper; - -import net.minecraft.world.server.ServerWorld; -import net.minecraftforge.common.WorldWorkerManager; - -/** - * A singleton that handles all long distance LOD world generation. - * @author James Seibel - * @version 9-25-2021 - */ -public class LodWorldGenerator -{ - public final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; - - /** This holds the thread used to generate new LODs off the main thread. */ - private final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator")); - - /** we only want to queue up one generator thread at a time */ - private boolean generatorThreadRunning = false; - - /** - * How many chunks to generate outside the player's view distance at one - * time. (or more specifically how many requests to make at one time). I - * multiply by 8 to make sure there is always a buffer of chunk requests, to - * make sure the CPU is always busy, and we can generate LODs as quickly as - * possible. - */ - public int maxChunkGenRequests; - - /** - * This keeps track of how many chunk generation requests are on going. This is - * to limit how many chunks are queued at once. To prevent chunks from being - * generated for a long time in an area the player is no longer in. - */ - public final AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0); - - public final Set positionsWaitingToBeGenerated = new HashSet<>(); - - /** - * Singleton copy of this object - */ - public static final LodWorldGenerator INSTANCE = new LodWorldGenerator(); - - - private LodWorldGenerator() - { - - } - - /** - * Queues up LodNodeGenWorkers for the given lodDimension. - * @param renderer needed so the LodNodeGenWorkers can flag that the - * buffers need to be rebuilt. - */ - public void queueGenerationRequests(LodDimension lodDim, LodRenderer renderer, LodBuilder lodBuilder) - { - if (LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get() != DistanceGenerationMode.NONE - && !generatorThreadRunning - && mc.hasSinglePlayerServer()) - { - // the thread is now running, don't queue up another thread - generatorThreadRunning = true; - - // just in case the config changed - maxChunkGenRequests = LodConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.get() * 8; - - Thread generatorThread = new Thread(() -> - { - try - { - // round the player's block position down to the nearest chunk BlockPos - int playerPosX = mc.getPlayer().blockPosition().getX(); - int playerPosZ = mc.getPlayer().blockPosition().getZ(); - - - //=======================================// - // fill in positionsWaitingToBeGenerated // - //=======================================// - - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension); - - PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate( - maxChunkGenRequests, - playerPosX, - playerPosZ); - - - byte detailLevel; - int posX; - int posZ; - int nearIndex = 0; - int farIndex = 0; - - for (int i = 0; i < posToGenerate.getNumberOfPos(); i++) - { - // I wish there was a way to compress this code, but I'm not aware of - // an easy way to do so. - - // add the near positions - if (posToGenerate.getNthDetail(nearIndex, true) != 0 && nearIndex < posToGenerate.getNumberOfNearPos()) - { - detailLevel = (byte) (posToGenerate.getNthDetail(nearIndex, true) - 1); - posX = posToGenerate.getNthPosX(nearIndex, true); - posZ = posToGenerate.getNthPosZ(nearIndex, true); - nearIndex++; - - ChunkPosWrapper chunkPos = new ChunkPosWrapper(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ)); - - // prevent generating the same chunk multiple times - if (positionsWaitingToBeGenerated.contains(chunkPos)) - continue; - - // don't add more to the generation queue then allowed - if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests) - break; - - positionsWaitingToBeGenerated.add(chunkPos); - numberOfChunksWaitingToGenerate.addAndGet(1); - LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld); - WorldWorkerManager.addWorker(genWorker); - } - - - // add the far positions - if (posToGenerate.getNthDetail(farIndex, false) != 0 && farIndex < posToGenerate.getNumberOfFarPos()) - { - detailLevel = (byte) (posToGenerate.getNthDetail(farIndex, false) - 1); - posX = posToGenerate.getNthPosX(farIndex, false); - posZ = posToGenerate.getNthPosZ(farIndex, false); - farIndex++; - - ChunkPosWrapper chunkPos = new ChunkPosWrapper(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ)); - - // don't add more to the generation queue then allowed - if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests) - continue; - //break; - - // prevent generating the same chunk multiple times - if (positionsWaitingToBeGenerated.contains(chunkPos)) - continue; - - positionsWaitingToBeGenerated.add(chunkPos); - numberOfChunksWaitingToGenerate.addAndGet(1); - LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld); - WorldWorkerManager.addWorker(genWorker); - } - } - - } - catch (Exception e) - { - // this shouldn't ever happen, but just in case - e.printStackTrace(); - } - finally - { - generatorThreadRunning = false; - } - }); - - mainGenThread.execute(generatorThread); - } // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning - } // queueGenerationRequests - -} diff --git a/src/main/java/com/seibel/lod/config/LodConfig.java b/src/main/java/com/seibel/lod/config/LodConfig.java deleted file mode 100644 index 004b1fab2..000000000 --- a/src/main/java/com/seibel/lod/config/LodConfig.java +++ /dev/null @@ -1,591 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.config; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.apache.commons.lang3.tuple.Pair; -import org.apache.logging.log4j.LogManager; - -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.electronwill.nightconfig.core.io.WritingMode; -import com.seibel.lod.ModInfo; -import com.seibel.lod.enums.BlockToAvoid; -import com.seibel.lod.enums.BufferRebuildTimes; -import com.seibel.lod.enums.DebugMode; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.enums.FogDistance; -import com.seibel.lod.enums.FogDrawOverride; -import com.seibel.lod.enums.GenerationPriority; -import com.seibel.lod.enums.GpuUploadMethod; -import com.seibel.lod.enums.HorizontalQuality; -import com.seibel.lod.enums.HorizontalResolution; -import com.seibel.lod.enums.HorizontalScale; -import com.seibel.lod.enums.LodTemplate; -import com.seibel.lod.enums.VanillaOverdraw; -import com.seibel.lod.enums.VerticalQuality; -import com.seibel.lod.util.LodUtil; - -import net.minecraftforge.common.ForgeConfigSpec; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.config.ModConfig; - -/** - * This handles any configuration the user has access to. - * @author Leonardo Amato - * @author James Seibel - * @version 10-25-2021 - */ -@Mod.EventBusSubscriber -public class LodConfig -{ - // CONFIG STRUCTURE - // -> Client - // | - // |-> Graphics - // | |-> QualityOption - // | |-> FogQualityOption - // | |-> AdvancedGraphicsOption - // | - // |-> World Generation - // | - // |-> Advanced Mod Option - // |-> Threads - // |-> Buffers - // |-> Debugging - - - - public static class Client - { - public final Graphics graphics; - public final WorldGenerator worldGenerator; - public final AdvancedModOptions advancedModOptions; - - - //================// - // Client Configs // - //================// - public Client(ForgeConfigSpec.Builder builder) - { - builder.push(this.getClass().getSimpleName()); - { - graphics = new Graphics(builder); - worldGenerator = new WorldGenerator(builder); - advancedModOptions = new AdvancedModOptions(builder); - } - builder.pop(); - } - - - //==================// - // Graphics Configs // - //==================// - public static class Graphics - { - - public final QualityOption qualityOption; - public final FogQualityOption fogQualityOption; - public final AdvancedGraphicsOption advancedGraphicsOption; - - Graphics(ForgeConfigSpec.Builder builder) - { - builder.comment("These settings control how the mod will look in game").push("Graphics"); - { - qualityOption = new QualityOption(builder); - advancedGraphicsOption = new AdvancedGraphicsOption(builder); - fogQualityOption = new FogQualityOption(builder); - } - builder.pop(); - } - - - public static class QualityOption - { - public final ForgeConfigSpec.EnumValue drawResolution; - - public final ForgeConfigSpec.IntValue lodChunkRenderDistance; - - public final ForgeConfigSpec.EnumValue verticalQuality; - - public final ForgeConfigSpec.EnumValue horizontalScale; - - public final ForgeConfigSpec.EnumValue horizontalQuality; - - - QualityOption(ForgeConfigSpec.Builder builder) - { - builder.comment("These settings control how detailed the fake chunks will be.").push(this.getClass().getSimpleName()); - - verticalQuality = builder - .comment("\n\n" - + " This indicates how detailed fake chunks will represent \n" - + " overhangs, caves, floating islands, ect. \n" - + " Higher options will use more memory and increase GPU usage. \n" - + " " + VerticalQuality.LOW + ": uses at max 2 columns per position. \n" - + " " + VerticalQuality.MEDIUM + ": uses at max 4 columns per position. \n" - + " " + VerticalQuality.HIGH + ": uses at max 8 columns per position. \n") - .defineEnum("Vertical Quality", VerticalQuality.MEDIUM); - - horizontalScale = builder - .comment("\n\n" - + " This indicates how quickly fake chunks drop off in quality. \n" - + " " + HorizontalScale.LOW + ": quality drops every " + HorizontalScale.LOW.distanceUnit / 16 + " chunks. \n" - + " " + HorizontalScale.MEDIUM + ": quality drops every " + HorizontalScale.MEDIUM.distanceUnit / 16 + " chunks. \n" - + " " + HorizontalScale.HIGH + ": quality drops every " + HorizontalScale.HIGH.distanceUnit / 16 + " chunks. \n") - .defineEnum("Horizontal Scale", HorizontalScale.MEDIUM); - - horizontalQuality = builder - .comment("\n\n" - + " This indicates the exponential base of the quadratic drop-off \n" - + " " + HorizontalQuality.LOWEST + ": base " + HorizontalQuality.LOWEST.quadraticBase + ". \n" - + " " + HorizontalQuality.LOW + ": base " + HorizontalQuality.LOW.quadraticBase + ". \n" - + " " + HorizontalQuality.MEDIUM + ": base " + HorizontalQuality.MEDIUM.quadraticBase + ". \n" - + " " + HorizontalQuality.HIGH + ": base " + HorizontalQuality.HIGH.quadraticBase + ". \n") - .defineEnum("Horizontal Quality", HorizontalQuality.MEDIUM); - - drawResolution = builder - .comment("\n\n" - + " What is the maximum detail fake chunks should be drawn at? \n" - + " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n" - + " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n" - + " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n" - + " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n" - + " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n") - .defineEnum("Block size", HorizontalResolution.BLOCK); - - lodChunkRenderDistance = builder - .comment("\n\n" - + " The mod's render distance, measured in chunks. \n") - .defineInRange("Lod Render Distance", 64, 32, 1024); - - builder.pop(); - } - } - - - public static class FogQualityOption - { - public final ForgeConfigSpec.EnumValue fogDistance; - - public final ForgeConfigSpec.EnumValue fogDrawOverride; - - public final ForgeConfigSpec.BooleanValue disableVanillaFog; - - FogQualityOption(ForgeConfigSpec.Builder builder) - { - - builder.comment("These settings control the fog quality.").push(this.getClass().getSimpleName()); - - fogDistance = builder - .comment("\n\n" - + " At what distance should Fog be drawn on the fake chunks? \n" - + " If the fog cuts off abruptly or you are using Optifine's \"fast\" fog option \n" - + " set this to " + FogDistance.NEAR + " or " + FogDistance.FAR + ". \n") - .defineEnum("Fog Distance", FogDistance.FAR); - - fogDrawOverride = builder - .comment("\n\n" - + " When should fog be drawn? \n" - + " " + FogDrawOverride.OPTIFINE_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.FANCY + ". \n" - + " " + FogDrawOverride.NO_FOG + ": Never draw fog on the LODs \n" - + " " + FogDrawOverride.FAST + ": Always draw fast fog on the LODs \n" - + " " + FogDrawOverride.FANCY + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n") - .defineEnum("Fog Draw Override", FogDrawOverride.FANCY); - - disableVanillaFog = builder - .comment("\n\n" - + " If true disable Minecraft's fog. \n\n" - + "" - + " Experimental! May cause issues with Sodium. \n\n" - + "" - + " Unlike Optifine or Sodium's fog disabling option this won't change \n" - + " performance (we don't actually disable the fog, we just tell it to render a infinite distance away). \n" - + " May or may not play nice with other mods that edit fog. \n") - .define("Experimental Disable Vanilla Fog", false); - - builder.pop(); - } - } - - - public static class AdvancedGraphicsOption - { - public final ForgeConfigSpec.EnumValue lodTemplate; - - public final ForgeConfigSpec.BooleanValue disableDirectionalCulling; - - public final ForgeConfigSpec.BooleanValue alwaysDrawAtMaxQuality; - - public final ForgeConfigSpec.EnumValue vanillaOverdraw; - - public final ForgeConfigSpec.EnumValue gpuUploadMethod; - - public final ForgeConfigSpec.BooleanValue useExtendedNearClipPlane; - - AdvancedGraphicsOption(ForgeConfigSpec.Builder builder) - { - - builder.comment("Advanced graphics option for the mod").push(this.getClass().getSimpleName()); - - lodTemplate = builder - .comment("\n\n" - + " How should the LODs be drawn? \n" - + " NOTE: Currently only " + LodTemplate.CUBIC + " is implemented! \n" - + " \n" - + " " + LodTemplate.CUBIC + ": LOD Chunks are drawn as rectangular prisms (boxes). \n" - + " " + LodTemplate.TRIANGULAR + ": LOD Chunks smoothly transition between other. \n" - + " " + LodTemplate.DYNAMIC + ": LOD Chunks smoothly transition between each other, \n" - + " " + " unless a neighboring chunk is at a significantly different height. \n") - .defineEnum("LOD Template", LodTemplate.CUBIC); - - disableDirectionalCulling = builder - .comment("\n\n" - + " If false fake chunks behind the player's camera \n" - + " aren't drawn, increasing performance. \n\n" - + "" - + " If true all LODs are drawn, even those behind \n" - + " the player's camera, decreasing performance. \n\n" - + "" - + " Disable this if you see LODs disappearing. \n" - + " (Which may happen if you are using a camera mod) \n") - .define("Disable Directional Culling", false); - - alwaysDrawAtMaxQuality = builder - .comment("\n\n" - + " Disable quality falloff, \n" - + " all fake chunks will be drawn at the highest \n" - + " available detail level. \n\n" - + " " - + " WARNING: \n" - + " This could cause a Out Of Memory crash on render \n" - + " distances higher than 128 \n") - .define("Always Use Max Quality", false); - - vanillaOverdraw = builder - .comment("\n\n" - + " How often should LODs be drawn on top of regular chunks? \n" - + " HALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves. \n\n" - + " " + VanillaOverdraw.NEVER + ": LODs won't render on top of vanilla chunks. \n" - + " " + VanillaOverdraw.BORDER + ": LODs will render only on the border of vanilla chunks preventing only some holes in the world. \n" - + " " + VanillaOverdraw.DYNAMIC + ": LODs will render on top of distant vanilla chunks to hide delayed loading. \n" - + " " + " More effective on higher render distances. \n" - + " " + " For vanilla render distances less than or equal to " + LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW + " \n" - + " " + " " + VanillaOverdraw.NEVER + " or " + VanillaOverdraw.ALWAYS + " may be used depending on the dimension. \n" - + " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing holes in the world. \n") - .defineEnum("Vanilla Overdraw", VanillaOverdraw.DYNAMIC); - - gpuUploadMethod = builder - .comment("\n\n" - + " What method should be used to upload geometry to the GPU? \n\\n" - + "" - + " " + GpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. \n" - + " " + GpuUploadMethod.SUB_DATA + ": Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. \n" - + " " + GpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. Possibly better than " + GpuUploadMethod.SUB_DATA + " if using a integrated GPU. \n") - .defineEnum("GPU Upload Method", GpuUploadMethod.BUFFER_STORAGE); - - // This is a temporary fix (like vanilla overdraw) - // hopefully we can remove both once we get individual chunk rendering figured out - useExtendedNearClipPlane = builder - .comment("\n\n" - + " Will prevent some overdraw issues, but may cause nearby fake chunks to render incorrectly \n" - + " especially when in/near an ocean. \n") - .define("Use Extended Near Clip Plane", false); - - - builder.pop(); - } - } - } - - - - - //========================// - // WorldGenerator Configs // - //========================// - public static class WorldGenerator - { - public final ForgeConfigSpec.EnumValue generationPriority; - public final ForgeConfigSpec.EnumValue distanceGenerationMode; - public final ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration; - public final ForgeConfigSpec.EnumValue blockToAvoid; - //public final ForgeConfigSpec.BooleanValue useExperimentalPreGenLoading; - - WorldGenerator(ForgeConfigSpec.Builder builder) - { - builder.comment("These settings control how fake chunks outside your normal view range are generated.").push("Generation"); - - generationPriority = builder - .comment("\n\n" - + " " + GenerationPriority.FAR_FIRST + " \n" - + " LODs are generated from low to high detail \n" - + " with a small priority for far away regions. \n" - + " This fills in the world fastest. \n\n" - + "" - + " " + GenerationPriority.NEAR_FIRST + " \n" - + " LODs are generated around the player \n" - + " in a spiral, similar to vanilla minecraft. \n") - .defineEnum("Generation Priority", GenerationPriority.FAR_FIRST); - - distanceGenerationMode = builder - .comment("\n\n" - + " Note: The times listed here are the amount of time it took \n" - + " one of the developer's PC to generate 1 chunk, \n" - + " and are included so you can compare the \n" - + " different generation options. Your mileage may vary. \n" - + "\n" - - + " " + DistanceGenerationMode.NONE + " \n" - + " Don't run the distance generator. \n" - - + "\n" - + " " + DistanceGenerationMode.BIOME_ONLY + " \n" - + " Only generate the biomes and use the biome's \n" - + " grass color, water color, or snow color. \n" - + " Doesn't generate height, everything is shown at sea level. \n" - + " Multithreaded - Fastest (2-5 ms) \n" - - + "\n" - + " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT + " \n" - + " Same as BIOME_ONLY, except instead \n" - + " of always using sea level as the LOD height \n" - + " different biome types (mountain, ocean, forest, etc.) \n" - + " use predetermined heights to simulate having height data. \n" - + " Multithreaded - Fastest (2-5 ms) \n" - - + "\n" - + " " + DistanceGenerationMode.SURFACE + " \n" - + " Generate the world surface, \n" - + " this does NOT include trees, \n" - + " or structures. \n" - + " Multithreaded - Faster (10-20 ms) \n" - - + "\n" - + " " + DistanceGenerationMode.FEATURES + " \n" - + " Generate everything except structures. \n" - + " WARNING: This may cause world generation bugs or instability! \n" - + " Multithreaded - Fast (15-20 ms) \n" - - + "\n" - + " " + DistanceGenerationMode.SERVER + " \n" - + " Ask the server to generate/load each chunk. \n" - + " This will show player made structures, which can \n" - + " be useful if you are adding the mod to a pre-existing world. \n" - + " This is the most compatible, but causes server/simulation lag. \n" - + " SingleThreaded - Slow (15-50 ms, with spikes up to 200 ms) \n") - .defineEnum("Distance Generation Mode", DistanceGenerationMode.SURFACE); - - allowUnstableFeatureGeneration = builder - .comment("\n\n" - + " When using the " + DistanceGenerationMode.FEATURES + " generation mode \n" - + " some features may not be thread safe, which could \n" - + " cause instability and crashes. \n" - + " By default (false) those features are skipped, \n" - + " improving stability, but decreasing how many features are \n" - + " actually generated. \n" - + " (for example: some tree generation is unstable, \n" - + " so some trees may not be generated.) \n" - + " By setting this to true, all features will be generated, \n" - + " but your game will be more unstable and crashes may occur. \n" - + " \n" - + " I would love to remove this option and always generate everything, \n" - + " but I'm not sure how to do that. \n" - + " If you are a Java wizard, check out the git issue here: \n" - + " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n") - .define("Allow Unstable Feature Generation", false); - - blockToAvoid = builder - .comment("\n\n" - + " " + BlockToAvoid.NONE + ": Use all blocks when generating fake chunks \n\n" - + "" - + " " + BlockToAvoid.NON_FULL + ": Only use full blocks when generating fake chunks (ignores slabs, lanterns, torches, grass, etc.) \n\n" - + "" - + " " + BlockToAvoid.NO_COLLISION + ": Only use solid blocks when generating fake chunks (ignores grass, torches, etc.) \n" - + "" - + " " + BlockToAvoid.BOTH + ": Only use full solid blocks when generating fake chunks \n" - + "\n") - .defineEnum("Block to avoid", BlockToAvoid.BOTH); - - /*useExperimentalPreGenLoading = builder - .comment("\n\n" - + " if a chunk has been pre-generated, then the mod would use the real chunk for the \n" - + "fake chunk creation. May require a deletion of the lod file to see the result. \n") - .define("Use pre-generated chunks", false);*/ - builder.pop(); - } - } - - - - - //============================// - // AdvancedModOptions Configs // - //============================// - public static class AdvancedModOptions - { - - public final Threading threading; - public final Debugging debugging; - public final Buffers buffers; - - public AdvancedModOptions(ForgeConfigSpec.Builder builder) - { - builder.comment("Advanced mod settings").push(this.getClass().getSimpleName()); - { - threading = new Threading(builder); - debugging = new Debugging(builder); - buffers = new Buffers(builder); - } - builder.pop(); - } - - public static class Threading - { - public final ForgeConfigSpec.IntValue numberOfWorldGenerationThreads; - public final ForgeConfigSpec.IntValue numberOfBufferBuilderThreads; - - Threading(ForgeConfigSpec.Builder builder) - { - builder.comment("These settings control how many CPU threads the mod uses for different tasks.").push(this.getClass().getSimpleName()); - - numberOfWorldGenerationThreads = builder - .comment("\n\n" - + " This is how many threads are used when generating LODs outside \n" - + " the normal render distance. \n" - + " If you experience stuttering when generating distant LODs, decrease \n" - + " this number. If you want to increase LOD generation speed, \n" - + " increase this number. \n\n" - + "" - + " The maximum value is the number of logical processors on your CPU. \n" - + " Requires a restart to take effect. \n") - .defineInRange("numberOfWorldGenerationThreads", Math.max(1, Runtime.getRuntime().availableProcessors() / 2), 1, Runtime.getRuntime().availableProcessors()); - - numberOfBufferBuilderThreads = builder - .comment("\n\n" - + " This is how many threads are used when building vertex buffers \n" - + " (The things sent to your GPU to draw the fake chunks). \n" - + " If you experience high CPU usage when NOT generating distant \n" - + " fake chunks, lower this number. \n" - + " \n" - + " The maximum value is the number of logical processors on your CPU. \n" - + " Requires a restart to take effect. \n") - .defineInRange("numberOfBufferBuilderThreads", Math.max(1, Runtime.getRuntime().availableProcessors() / 2), 1, Runtime.getRuntime().availableProcessors()); - - builder.pop(); - } - } - - - - - //===============// - // Debug Options // - //===============// - public static class Debugging - { - public final ForgeConfigSpec.BooleanValue drawLods; - public final ForgeConfigSpec.EnumValue debugMode; - public final ForgeConfigSpec.BooleanValue enableDebugKeybindings; - - Debugging(ForgeConfigSpec.Builder builder) - { - builder.comment("These settings can be used to look for bugs, or see how certain aspects of the mod work.").push(this.getClass().getSimpleName()); - - drawLods = builder - .comment("\n\n" - + " If true, the mod is enabled and fake chunks will be drawn. \n" - + " If false, the mod will still generate fake chunks, \n" - + " but they won't be rendered. \n") - .define("Enable Rendering", true); - - debugMode = builder - .comment("\n\n" - + " " + DebugMode.OFF + ": Fake chunks will be drawn with their normal colors. \n" - + " " + DebugMode.SHOW_DETAIL + ": Fake chunks color will be based on their detail level. \n" - + " " + DebugMode.SHOW_DETAIL_WIREFRAME + ": Fake chunks color will be based on their detail level, drawn as a wireframe. \n") - .defineEnum("Debug Mode", DebugMode.OFF); - - enableDebugKeybindings = builder - .comment("\n\n" - + " If true the F4 key can be used to cycle through the different debug modes. \n" - + " and the F6 key can be used to enable and disable LOD rendering.") - .define("Enable Debug Keybinding", false); - - builder.pop(); - } - } - - - public static class Buffers - { - public final ForgeConfigSpec.EnumValue rebuildTimes; - - Buffers(ForgeConfigSpec.Builder builder) - { - builder.comment("These settings affect how often geometry is are built.").push(this.getClass().getSimpleName()); - - rebuildTimes = builder - .comment("\n\n" - + " How frequently should geometry be rebuilt and sent to the GPU? \n" - + " Higher settings may cause stuttering, but will prevent holes in the world \n") - .defineEnum("rebuildFrequency", BufferRebuildTimes.NORMAL); - - builder.pop(); - } - } - } - } - - - /** {@link Path} to the configuration file of this mod */ - private static final Path CONFIG_PATH = Paths.get("config", ModInfo.NAME + ".toml"); - - public static final ForgeConfigSpec CLIENT_SPEC; - public static final Client CLIENT; - - static - { - final Pair specPair = new ForgeConfigSpec.Builder().configure(Client::new); - CLIENT_SPEC = specPair.getRight(); - CLIENT = specPair.getLeft(); - CommentedFileConfig clientConfig = CommentedFileConfig.builder(CONFIG_PATH) - .writingMode(WritingMode.REPLACE) - .build(); - clientConfig.load(); - clientConfig.save(); - CLIENT_SPEC.setConfig(clientConfig); - } - - @SubscribeEvent - public static void onLoad(final ModConfig.Loading configEvent) - { - LogManager.getLogger().debug(ModInfo.NAME, "Loaded forge config file {}", configEvent.getConfig().getFileName()); - } - - @SubscribeEvent - public static void onFileChange(final ModConfig.Reloading configEvent) - { - LogManager.getLogger().debug(ModInfo.NAME, "Forge config just got changed on the file system!"); - } - -} diff --git a/src/main/java/com/seibel/lod/enums/BlockToAvoid.java b/src/main/java/com/seibel/lod/enums/BlockToAvoid.java deleted file mode 100644 index a09b5d68f..000000000 --- a/src/main/java/com/seibel/lod/enums/BlockToAvoid.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * heightmap
- * multi_lod
- * - * @author Leonardo Amato - * @version 19-10-2021 - */ -public enum BlockToAvoid -{ - NONE(false, false), - - NON_FULL(true, false), - - NO_COLLISION(false, true), - - BOTH(true, true); - - public final boolean nonFull; - public final boolean noCollision; - - BlockToAvoid(boolean nonFull, boolean noCollision) - { - this.nonFull = nonFull; - this.noCollision = noCollision; - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/BufferRebuildTimes.java b/src/main/java/com/seibel/lod/enums/BufferRebuildTimes.java deleted file mode 100644 index 0ae515b1f..000000000 --- a/src/main/java/com/seibel/lod/enums/BufferRebuildTimes.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * Near_First
- * Far_First
- *
- * Determines how fast the buffers need to be regenerated - * - * @author Leonardo Amato - * @version 9-25-2021 - */ -public enum BufferRebuildTimes -{ - FREQUENT(1000, 500, 2500), - - NORMAL(2000, 1000, 5000), - - RARE(5000, 2000, 10000); - - public final int playerMoveTimeout; - public final int renderedChunkTimeout; - public final int chunkChangeTimeout; - - BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout) - { - this.playerMoveTimeout = playerMoveTimeout; - this.renderedChunkTimeout = renderedChunkTimeout; - this.chunkChangeTimeout = chunkChangeTimeout; - } -} diff --git a/src/main/java/com/seibel/lod/enums/DistanceGenerationMode.java b/src/main/java/com/seibel/lod/enums/DistanceGenerationMode.java deleted file mode 100644 index 3060ec4a4..000000000 --- a/src/main/java/com/seibel/lod/enums/DistanceGenerationMode.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * NONE
- * BIOME_ONLY
- * BIOME_ONLY_SIMULATE_HEIGHT
- * SURFACE
- * FEATURES
- * SERVER

- *

- * In order of fastest to slowest. - * - * @author James Seibel - * @author Leonardo Amato - * @version 8-7-2021 - */ -public enum DistanceGenerationMode -{ - /** - * Don't generate anything - */ - NONE((byte) 0), - - /** - * Only generate the biomes and use biome - * grass/foliage color, water color, or ice color - * to generate the color. - * Doesn't generate height, everything is shown at sea level. - * Multithreaded - Fastest (2-5 ms) - */ - BIOME_ONLY((byte) 1), - - /** - * Same as BIOME_ONLY, except instead - * of always using sea level as the LOD height - * different biome types (mountain, ocean, forest, etc.) - * use predetermined heights to simulate having height data. - */ - BIOME_ONLY_SIMULATE_HEIGHT((byte) 2), - - /** - * Generate the world surface, - * this does NOT include caves, trees, - * or structures. - * Multithreaded - Faster (10-20 ms) - */ - SURFACE((byte) 3), - - /** - * Generate everything except structures. - * NOTE: This may cause world generation bugs or instability, - * since some features cause concurrentModification exceptions. - * Multithreaded - Fast (15-20 ms) - */ - FEATURES((byte) 4), - - /** - * Ask the server to generate/load each chunk. - * This is the most compatible, but causes server/simulation lag. - * This will also show player made structures if you - * are adding the mod to a pre-existing world. - * Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) - */ - SERVER((byte) 5); - - - /** - * The higher the number the more complete the generation is. - */ - public final byte complexity; - - DistanceGenerationMode(byte complexity) - { - this.complexity = complexity; - } -} diff --git a/src/main/java/com/seibel/lod/enums/FogDistance.java b/src/main/java/com/seibel/lod/enums/FogDistance.java deleted file mode 100644 index b589a57c5..000000000 --- a/src/main/java/com/seibel/lod/enums/FogDistance.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * NEAR, FAR, or NEAR_AND_FAR. - * - * @author James Seibel - * @version 02-14-2021 - */ -public enum FogDistance -{ - /** good for fast or fancy fog qualities. */ - NEAR, - - /** good for fast or fancy fog qualities. */ - FAR, - - /** only looks good if the fog quality is set to Fancy. */ - NEAR_AND_FAR -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/FogDrawOverride.java b/src/main/java/com/seibel/lod/enums/FogDrawOverride.java deleted file mode 100644 index 57ec7a3af..000000000 --- a/src/main/java/com/seibel/lod/enums/FogDrawOverride.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * USE_OPTIFINE_FOG_SETTING,
- * NEVER_DRAW_FOG,
- * ALWAYS_DRAW_FOG_FAST,
- * ALWAYS_DRAW_FOG_FANCY
- * - * @author James Seibel - * @version 7-3-2021 - */ -public enum FogDrawOverride -{ - /** - * Use whatever Fog setting optifine is using. - * If optifine isn't installed this defaults to ALWAYS_DRAW_FOG. - */ - OPTIFINE_SETTING, - - /** Never draw fog on the LODs */ - NO_FOG, - - /** Always draw fast fog on the LODs */ - FAST, - - /** Always draw fancy fog on the LODs */ - FANCY -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/GenerationPriority.java b/src/main/java/com/seibel/lod/enums/GenerationPriority.java deleted file mode 100644 index 63c6b9d7f..000000000 --- a/src/main/java/com/seibel/lod/enums/GenerationPriority.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * Near_First
- * Far_First
- *
- * Determines which LODs should have priority when generating - * outside the normal view distance. - * - * @author Leonardo Amato - * @version 9-25-2021 - */ -public enum GenerationPriority -{ - NEAR_FIRST, - - FAR_FIRST -} diff --git a/src/main/java/com/seibel/lod/enums/GlProxyContext.java b/src/main/java/com/seibel/lod/enums/GlProxyContext.java deleted file mode 100644 index 512b46c7b..000000000 --- a/src/main/java/com/seibel/lod/enums/GlProxyContext.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * Minecraft, Lod_Builder, None - * - * @author James Seibel - * @version 10-1-2021 - */ -public enum GlProxyContext -{ - /** Minecraft's render thread */ - MINECRAFT, - - /** The context we send buffers to the GPU on */ - LOD_BUILDER, - - /** used to un-bind threads */ - NONE, -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/GpuUploadMethod.java b/src/main/java/com/seibel/lod/enums/GpuUploadMethod.java deleted file mode 100644 index b88be18be..000000000 --- a/src/main/java/com/seibel/lod/enums/GpuUploadMethod.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * Buffer_Storage, Sub_Data, Buffer_Mapping - * - * @author James Seibel - * @version 10-23-2021 - */ -public enum GpuUploadMethod -{ - /** Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. */ - BUFFER_STORAGE, - - /** Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. */ - SUB_DATA, - - /** May end up storing buffers in System memory. Slower rendering but won't stutter when uploading. */ - BUFFER_MAPPING, -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/HorizontalQuality.java b/src/main/java/com/seibel/lod/enums/HorizontalQuality.java deleted file mode 100644 index 09472c320..000000000 --- a/src/main/java/com/seibel/lod/enums/HorizontalQuality.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * Lowest
- * Low
- * Medium
- * High
- *
- * this indicates the base of the quadratic function we use for the quality drop off - * - * @author Leonardo Amato - * @version 9-29-2021 - */ -public enum HorizontalQuality -{ - /** 1.0 AKA Linear */ - LOWEST(1.0f), - - /** exponent 1.5 */ - LOW(1.5f), - - /** exponent 2.0 */ - MEDIUM(2.0f), - - /** exponent 2.2 */ - HIGH(2.2f); - - public final double quadraticBase; - - HorizontalQuality(double distanceUnit) - { - this.quadraticBase = distanceUnit; - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/HorizontalResolution.java b/src/main/java/com/seibel/lod/enums/HorizontalResolution.java deleted file mode 100644 index e8cdbbfa9..000000000 --- a/src/main/java/com/seibel/lod/enums/HorizontalResolution.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -import java.util.ArrayList; -import java.util.Collections; - -import com.seibel.lod.util.LodUtil; - -/** - * chunk
- * half_chunk
- * four_blocks
- * two_blocks
- * block
- * - * @author James Seibel - * @author Leonardo Amato - * @version 9-25-2021 - */ -public enum HorizontalResolution -{ - /** render 256 LODs for each chunk */ - BLOCK(16, 0), - - /** render 64 LODs for each chunk */ - TWO_BLOCKS(8, 1), - - /** render 16 LODs for each chunk */ - FOUR_BLOCKS(4, 2), - - /** render 4 LODs for each chunk */ - HALF_CHUNK(2, 3), - - /** render 1 LOD for each chunk */ - CHUNK(1, 4); - - - - - - /** - * How many DataPoints should - * be drawn per side, per LodChunk - */ - public final int dataPointLengthCount; - - /** How wide each LOD DataPoint is */ - public final int dataPointWidth; - - /** - * This is the same as detailLevel in LodQuadTreeNode, - * lowest is 0 highest is 9 - */ - public final byte detailLevel; - - /* Start/End X/Z give the block positions - * for each individual dataPoint in a LodChunk */ - public final int[] startX; - public final int[] startZ; - - public final int[] endX; - public final int[] endZ; - - - /** - * 1st dimension: LodDetail.detailLevel
- * 2nd dimension: An array of all LodDetails that are less than or
- * equal to that detailLevel - */ - private static HorizontalResolution[][] lowerDetailArrays; - - - - - HorizontalResolution(int newLengthCount, int newDetailLevel) - { - detailLevel = (byte) newDetailLevel; - dataPointLengthCount = newLengthCount; - dataPointWidth = 16 / dataPointLengthCount; - - startX = new int[dataPointLengthCount * dataPointLengthCount]; - endX = new int[dataPointLengthCount * dataPointLengthCount]; - - startZ = new int[dataPointLengthCount * dataPointLengthCount]; - endZ = new int[dataPointLengthCount * dataPointLengthCount]; - - - int index = 0; - for (int x = 0; x < newLengthCount; x++) - { - for (int z = 0; z < newLengthCount; z++) - { - startX[index] = x * dataPointWidth; - startZ[index] = z * dataPointWidth; - - endX[index] = (x * dataPointWidth) + dataPointWidth; - endZ[index] = (z * dataPointWidth) + dataPointWidth; - - index++; - } - } - - }// constructor - - - - - - - /** - * Returns an array of all LodDetails that have a detail level - * that is less than or equal to the given LodDetail - */ - public static HorizontalResolution[] getSelfAndLowerDetails(HorizontalResolution detail) - { - if (lowerDetailArrays == null) - { - // run first time setup - lowerDetailArrays = new HorizontalResolution[HorizontalResolution.values().length][]; - - // go through each LodDetail - for (HorizontalResolution currentDetail : HorizontalResolution.values()) - { - ArrayList lowerDetails = new ArrayList<>(); - - // find the details lower than currentDetail - for (HorizontalResolution compareDetail : HorizontalResolution.values()) - { - if (currentDetail.detailLevel <= compareDetail.detailLevel) - { - lowerDetails.add(compareDetail); - } - } - - // have the highest detail item first in the list - Collections.sort(lowerDetails); - Collections.reverse(lowerDetails); - - lowerDetailArrays[currentDetail.detailLevel] = lowerDetails.toArray(new HorizontalResolution[lowerDetails.size()]); - } - } - - return lowerDetailArrays[detail.detailLevel]; - } - - /** Returns what detail level should be used at a given distance and maxDistance. */ - public static HorizontalResolution getDetailForDistance(HorizontalResolution maxDetailLevel, int distance, int maxDistance) - { - HorizontalResolution[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel); - int distanceBetweenDetails = maxDistance / lowerDetails.length; - int index = LodUtil.clamp(0, distance / distanceBetweenDetails, lowerDetails.length - 1); - - return lowerDetails[index]; - - } - -} diff --git a/src/main/java/com/seibel/lod/enums/HorizontalScale.java b/src/main/java/com/seibel/lod/enums/HorizontalScale.java deleted file mode 100644 index cf941d1e6..000000000 --- a/src/main/java/com/seibel/lod/enums/HorizontalScale.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * Low
- * Medium
- * High
- *
- * this is a quality scale for the detail drop-off - * - * @author Leonardo Amato - * @version 9-25-2021 - */ -public enum HorizontalScale -{ - /** Lods are 2D with heightMap */ - LOW(64), - - /** Lods expand in three dimension */ - MEDIUM(128), - - /** Lods expand in three dimension */ - HIGH(256); - - public final int distanceUnit; - - HorizontalScale(int distanceUnit) - { - this.distanceUnit = distanceUnit; - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/LodTemplate.java b/src/main/java/com/seibel/lod/enums/LodTemplate.java deleted file mode 100644 index 965838571..000000000 --- a/src/main/java/com/seibel/lod/enums/LodTemplate.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -import com.seibel.lod.builders.bufferBuilding.lodTemplates.AbstractLodTemplate; -import com.seibel.lod.builders.bufferBuilding.lodTemplates.CubicLodTemplate; -import com.seibel.lod.builders.bufferBuilding.lodTemplates.DynamicLodTemplate; -import com.seibel.lod.builders.bufferBuilding.lodTemplates.TriangularLodTemplate; - -/** - * Cubic, Triangular, Dynamic - * - * @author James Seibel - * @version 10-10-2021 - */ -public enum LodTemplate -{ - /** - * LODs are rendered as - * rectangular prisms. - */ - CUBIC(new CubicLodTemplate()), - - /** - * LODs smoothly transition between - * each other. - */ - TRIANGULAR(new TriangularLodTemplate()), - - /** - * LODs smoothly transition between - * each other, unless a neighboring LOD - * is at a significantly different height. - */ - DYNAMIC(new DynamicLodTemplate()); - - - public final AbstractLodTemplate template; - - LodTemplate(AbstractLodTemplate newTemplate) - { - template = newTemplate; - } - -} diff --git a/src/main/java/com/seibel/lod/enums/ShadingMode.java b/src/main/java/com/seibel/lod/enums/ShadingMode.java deleted file mode 100644 index 89d7d0405..000000000 --- a/src/main/java/com/seibel/lod/enums/ShadingMode.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.seibel.lod.enums; - -/** - * NONE, GAME_SHADING - * - * @author James Seibel - * @version 7-25-2020 - */ -public enum ShadingMode -{ - /** - * LODs will have darker sides and bottoms to simulate - * Minecraft's fast lighting. - */ - GAME_SHADING, - - /** - * LODs will use ambient occlusion to mimic Minecarft's - * Fancy lighting. - */ - AMBIENT_OCCLUSION -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/VanillaOverdraw.java b/src/main/java/com/seibel/lod/enums/VanillaOverdraw.java deleted file mode 100644 index aa980a6ff..000000000 --- a/src/main/java/com/seibel/lod/enums/VanillaOverdraw.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * None, Dynamic, Always - * - *

- * This represents how far the LODs should overlap with - * the vanilla Minecraft terrain. - * - * @author James Seibel - * @version 10-11-2021 - */ -public enum VanillaOverdraw -{ - /** Never draw LODs where a minecraft chunk could be. */ - NEVER, - - /** Draw LODs over the farther minecraft chunks. */ - DYNAMIC, - - /** Draw LODs over all minecraft chunks. */ - ALWAYS, - - /** Draw LODs over border chunks. */ - BORDER, -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/VerticalQuality.java b/src/main/java/com/seibel/lod/enums/VerticalQuality.java deleted file mode 100644 index b66cff165..000000000 --- a/src/main/java/com/seibel/lod/enums/VerticalQuality.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.enums; - -/** - * heightmap
- * multi_lod
- * - * @author Leonardo Amato - * @version 10-07-2021 - */ -public enum VerticalQuality -{ - LOW( - new int[] { 2, - 2, - 2, - 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1 } - ), - - MEDIUM( - new int[] { 4, - 4, - 2, - 2, - 2, - 1, - 1, - 1, - 1, - 1, - 1 } - ), - - HIGH( - new int[] { - 8, - 8, - 4, - 4, - 2, - 2, - 2, - 1, - 1, - 1, - 1 } - ); - - public final int[] maxVerticalData; - - VerticalQuality(int[] maxVerticalData) - { - this.maxVerticalData = maxVerticalData; - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/forge/ForgeClientProxy.java b/src/main/java/com/seibel/lod/forge/ForgeClientProxy.java new file mode 100644 index 000000000..4ddf72131 --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/ForgeClientProxy.java @@ -0,0 +1,105 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.forge; + +import com.seibel.lod.core.api.EventApi; +import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper; +import com.seibel.lod.forge.wrappers.world.DimensionTypeWrapper; +import com.seibel.lod.forge.wrappers.world.WorldWrapper; + +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.ChunkEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +/** + * This handles all events sent to the client, + * and is the starting point for most of the mod. + * + * @author James_Seibel + * @version 11-12-2021 + */ +public class ForgeClientProxy +{ + private final EventApi eventApi = EventApi.INSTANCE; + + + + @SubscribeEvent + public void serverTickEvent(TickEvent.ServerTickEvent event) + { + eventApi.serverTickEvent(); + } + + @SubscribeEvent + public void chunkLoadEvent(ChunkEvent.Load event) + { + eventApi.chunkLoadEvent(new ChunkWrapper(event.getChunk()), DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType())); + } + + @SubscribeEvent + public void worldSaveEvent(WorldEvent.Save event) + { + eventApi.worldSaveEvent(); + } + + /** This is also called when a new dimension loads */ + @SubscribeEvent + public void worldLoadEvent(WorldEvent.Load event) + { + eventApi.worldLoadEvent(WorldWrapper.getWorldWrapper(event.getWorld())); + } + + @SubscribeEvent + public void worldUnloadEvent(WorldEvent.Unload event) + { + eventApi.worldUnloadEvent(); + } + + @SubscribeEvent + public void blockChangeEvent(BlockEvent event) + { + // we only care about certain block events + if (event.getClass() == BlockEvent.BreakEvent.class || + event.getClass() == BlockEvent.EntityPlaceEvent.class || + event.getClass() == BlockEvent.EntityMultiPlaceEvent.class || + event.getClass() == BlockEvent.FluidPlaceBlockEvent.class || + event.getClass() == BlockEvent.PortalSpawnEvent.class) + { + IChunkWrapper chunk = new ChunkWrapper(event.getWorld().getChunk(event.getPos())); + DimensionTypeWrapper dimType = DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType()); + + // recreate the LOD where the blocks were changed + eventApi.blockChangeEvent(chunk, dimType); + } + } + + @SubscribeEvent + public void onKeyInput(InputEvent.KeyInputEvent event) + { + eventApi.onKeyInput(event.getKey(), event.getAction()); + } + + + +} diff --git a/src/main/java/com/seibel/lod/forge/ForgeConfig.java b/src/main/java/com/seibel/lod/forge/ForgeConfig.java new file mode 100644 index 000000000..4bf59c8f9 --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/ForgeConfig.java @@ -0,0 +1,437 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.forge; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.LogManager; + +import com.electronwill.nightconfig.core.file.CommentedFileConfig; +import com.electronwill.nightconfig.core.io.WritingMode; +import com.seibel.lod.core.ModInfo; +import com.seibel.lod.core.enums.config.BlocksToAvoid; +import com.seibel.lod.core.enums.config.BufferRebuildTimes; +import com.seibel.lod.core.enums.config.DistanceGenerationMode; +import com.seibel.lod.core.enums.config.GenerationPriority; +import com.seibel.lod.core.enums.config.GpuUploadMethod; +import com.seibel.lod.core.enums.config.HorizontalQuality; +import com.seibel.lod.core.enums.config.HorizontalResolution; +import com.seibel.lod.core.enums.config.HorizontalScale; +import com.seibel.lod.core.enums.config.LodTemplate; +import com.seibel.lod.core.enums.config.VanillaOverdraw; +import com.seibel.lod.core.enums.config.VerticalQuality; +import com.seibel.lod.core.enums.rendering.DebugMode; +import com.seibel.lod.core.enums.rendering.FogDistance; +import com.seibel.lod.core.enums.rendering.FogDrawOverride; +import com.seibel.lod.core.objects.MinDefaultMax; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IWorldGenerator; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IBuffers; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IDebugging; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.IThreading; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IAdvancedGraphics; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IFogQuality; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IGraphics.IQuality; + +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; + +/** + * This handles any configuration the user has access to. + * @author Leonardo Amato + * @author James Seibel + * @version 11-16-2021 + */ +@Mod.EventBusSubscriber +public class ForgeConfig +{ + // CONFIG STRUCTURE + // -> Client + // | + // |-> Graphics + // | |-> QualityOption + // | |-> FogQualityOption + // | |-> AdvancedGraphicsOption + // | + // |-> World Generation + // | + // |-> Advanced Mod Option + // |-> Threads + // |-> Buffers + // |-> Debugging + + + + public static class Client + { + public final Graphics graphics; + public final WorldGenerator worldGenerator; + public final AdvancedModOptions advancedModOptions; + + + //================// + // Client Configs // + //================// + public Client(ForgeConfigSpec.Builder builder) + { + builder.push(this.getClass().getSimpleName()); + { + graphics = new Graphics(builder); + worldGenerator = new WorldGenerator(builder); + advancedModOptions = new AdvancedModOptions(builder); + } + builder.pop(); + } + + + //==================// + // Graphics Configs // + //==================// + public static class Graphics + { + public final QualityOption qualityOption; + public final FogQualityOption fogQuality; + public final AdvancedGraphicsOption advancedGraphicsOption; + + Graphics(ForgeConfigSpec.Builder builder) + { + builder.comment(IGraphics.DESC).push("Graphics"); + { + qualityOption = new QualityOption(builder); + advancedGraphicsOption = new AdvancedGraphicsOption(builder); + fogQuality = new FogQualityOption(builder); + } + builder.pop(); + } + + + public static class QualityOption + { + public final ForgeConfigSpec.EnumValue drawResolution; + public final ForgeConfigSpec.IntValue lodChunkRenderDistance; + public final ForgeConfigSpec.EnumValue verticalQuality; + public final ForgeConfigSpec.EnumValue horizontalScale; + public final ForgeConfigSpec.EnumValue horizontalQuality; + + QualityOption(ForgeConfigSpec.Builder builder) + { + builder.comment(IQuality.DESC).push(this.getClass().getSimpleName()); + + verticalQuality = builder + .comment("\n\n" + + IQuality.VERTICAL_QUALITY_DESC) + .defineEnum("Vertical Quality", IQuality.VERTICAL_QUALITY_DEFAULT); + + horizontalScale = builder + .comment("\n\n" + + IQuality.HORIZONTAL_SCALE_DESC) + .defineEnum("Horizontal Scale", IQuality.HORIZONTAL_SCALE_DEFAULT); + + horizontalQuality = builder + .comment("\n\n" + + IQuality.HORIZONTAL_QUALITY_DESC) + .defineEnum("Horizontal Quality", IQuality.HORIZONTAL_QUALITY_DEFAULT); + + drawResolution = builder + .comment("\n\n" + + IQuality.DRAW_RESOLUTION_DESC) + .defineEnum("Block size", IQuality.DRAW_RESOLUTION_DEFAULT); + + MinDefaultMax minDefaultMax = IQuality.LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX; + lodChunkRenderDistance = builder + .comment("\n\n" + + IQuality.LOD_CHUNK_RENDER_DISTANCE_DESC) + .defineInRange("Lod Render Distance", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue); + + builder.pop(); + } + } + + + public static class FogQualityOption + { + public final ForgeConfigSpec.EnumValue fogDistance; + public final ForgeConfigSpec.EnumValue fogDrawOverride; + public final ForgeConfigSpec.BooleanValue disableVanillaFog; + + FogQualityOption(ForgeConfigSpec.Builder builder) + { + builder.comment(IFogQuality.DESC).push(this.getClass().getSimpleName()); + + fogDistance = builder + .comment("\n\n" + + IFogQuality.FOG_DISTANCE_DESC) + .defineEnum("Fog Distance", IFogQuality.FOG_DISTANCE_DEFAULT); + + fogDrawOverride = builder + .comment("\n\n" + + IFogQuality.FOG_DRAW_OVERRIDE_DESC) + .defineEnum("Fog Draw Override", IFogQuality.FOG_DRAW_OVERRIDE_DEFAULT); + + disableVanillaFog = builder + .comment("\n\n" + + IFogQuality.DISABLE_VANILLA_FOG_DESC) + .define("Experimental Disable Vanilla Fog", IFogQuality.DISABLE_VANILLA_FOG_DEFAULT); + + builder.pop(); + } + } + + + public static class AdvancedGraphicsOption + { + public final ForgeConfigSpec.EnumValue lodTemplate; + public final ForgeConfigSpec.BooleanValue disableDirectionalCulling; + public final ForgeConfigSpec.BooleanValue alwaysDrawAtMaxQuality; + public final ForgeConfigSpec.EnumValue vanillaOverdraw; + public final ForgeConfigSpec.EnumValue gpuUploadMethod; + public final ForgeConfigSpec.BooleanValue useExtendedNearClipPlane; + + AdvancedGraphicsOption(ForgeConfigSpec.Builder builder) + { + builder.comment(IAdvancedGraphics.DESC).push(this.getClass().getSimpleName()); + + lodTemplate = builder + .comment("\n\n" + + IAdvancedGraphics.LOD_TEMPLATE_DESC) + .defineEnum("LOD Template", IAdvancedGraphics.LOD_TEMPLATE_DEFAULT); + + disableDirectionalCulling = builder + .comment("\n\n" + + IAdvancedGraphics.DISABLE_DIRECTIONAL_CULLING_DESC) + .define("Disable Directional Culling", IAdvancedGraphics.DISABLE_DIRECTIONAL_CULLING_DEFAULT); + + alwaysDrawAtMaxQuality = builder + .comment("\n\n" + + IAdvancedGraphics.ALWAYS_DRAW_AT_MAD_QUALITY_DESC) + .define("Always Use Max Quality", IAdvancedGraphics.ALWAYS_DRAW_AT_MAD_QUALITY_DEFAULT); + + vanillaOverdraw = builder + .comment("\n\n" + + IAdvancedGraphics.VANILLA_OVERDRAW_DESC) + .defineEnum("Vanilla Overdraw", IAdvancedGraphics.VANILLA_OVERDRAW_DEFAULT); + + gpuUploadMethod = builder + .comment("\n\n" + + IAdvancedGraphics.GPU_UPLOAD_METHOD_DESC) + .defineEnum("GPU Upload Method", IAdvancedGraphics.GPU_UPLOAD_METHOD_DEFAULT); + + // This is a temporary fix (like vanilla overdraw) + // hopefully we can remove both once we get individual chunk rendering figured out + useExtendedNearClipPlane = builder + .comment("\n\n" + + IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DESC) + .define("Use Extended Near Clip Plane", IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT); + + + builder.pop(); + } + } + } + + + + + //========================// + // WorldGenerator Configs // + //========================// + public static class WorldGenerator + { + public final ForgeConfigSpec.EnumValue generationPriority; + public final ForgeConfigSpec.EnumValue distanceGenerationMode; + public final ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration; + public final ForgeConfigSpec.EnumValue blocksToAvoid; + //public final ForgeConfigSpec.BooleanValue useExperimentalPreGenLoading; + + WorldGenerator(ForgeConfigSpec.Builder builder) + { + builder.comment(IWorldGenerator.DESC).push("Generation"); + + generationPriority = builder + .comment("\n\n" + + IWorldGenerator.GENERATION_PRIORITY_DESC) + .defineEnum("Generation Priority", IWorldGenerator.GENERATION_PRIORITY_DEFAULT); + + distanceGenerationMode = builder + .comment("\n\n" + + IWorldGenerator.DISTANCE_GENERATION_MODE_DESC) + .defineEnum("Distance Generation Mode", IWorldGenerator.DISTANCE_GENERATION_MODE_DEFAULT); + + allowUnstableFeatureGeneration = builder + .comment("\n\n" + + IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DESC) + .define("Allow Unstable Feature Generation", IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT); + + blocksToAvoid = builder + .comment("\n\n" + + IWorldGenerator.BLOCKS_TO_AVOID_DESC) + .defineEnum("Blocks to avoid", IWorldGenerator.BLOCKS_TO_AVOID_DEFAULT); + + /*useExperimentalPreGenLoading = builder + .comment("\n\n" + + " if a chunk has been pre-generated, then the mod would use the real chunk for the \n" + + "fake chunk creation. May require a deletion of the lod file to see the result. \n") + .define("Use pre-generated chunks", false);*/ + builder.pop(); + } + } + + + + + //============================// + // AdvancedModOptions Configs // + //============================// + public static class AdvancedModOptions + { + public final Threading threading; + public final Debugging debugging; + public final Buffers buffers; + + public AdvancedModOptions(ForgeConfigSpec.Builder builder) + { + builder.comment(IAdvanced.DESC).push(this.getClass().getSimpleName()); + { + threading = new Threading(builder); + debugging = new Debugging(builder); + buffers = new Buffers(builder); + } + builder.pop(); + } + + public static class Threading + { + public final ForgeConfigSpec.IntValue numberOfWorldGenerationThreads; + public final ForgeConfigSpec.IntValue numberOfBufferBuilderThreads; + + Threading(ForgeConfigSpec.Builder builder) + { + builder.comment(IThreading.DESC).push(this.getClass().getSimpleName()); + + MinDefaultMax minDefaultMax = IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT; + numberOfWorldGenerationThreads = builder + .comment("\n\n" + + IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DESC) + .defineInRange("numberOfWorldGenerationThreads", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue); + + + minDefaultMax = IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX; + numberOfBufferBuilderThreads = builder + .comment("\n\n" + + IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX) + .defineInRange("numberOfBufferBuilderThreads", minDefaultMax.defaultValue, minDefaultMax.minValue, minDefaultMax.maxValue); + + builder.pop(); + } + } + + + + + //===============// + // Debug Options // + //===============// + public static class Debugging + { + public final ForgeConfigSpec.BooleanValue drawLods; + public final ForgeConfigSpec.EnumValue debugMode; + public final ForgeConfigSpec.BooleanValue enableDebugKeybindings; + + Debugging(ForgeConfigSpec.Builder builder) + { + builder.comment(IDebugging.DESC).push(this.getClass().getSimpleName()); + + drawLods = builder + .comment("\n\n" + + IDebugging.DRAW_LODS_DESC) + .define("Enable Rendering", IDebugging.DRAW_LODS_DEFAULT); + + debugMode = builder + .comment("\n\n" + + IDebugging.DEBUG_MODE_DESC) + .defineEnum("Debug Mode", IDebugging.DEBUG_MODE_DEFAULT); + + enableDebugKeybindings = builder + .comment("\n\n" + + IDebugging.DEBUG_KEYBINDINGS_ENABLED_DESC) + .define("Enable Debug Keybinding", IDebugging.DEBUG_KEYBINDINGS_ENABLED_DEFAULT); + + builder.pop(); + } + } + + + public static class Buffers + { + public final ForgeConfigSpec.EnumValue rebuildTimes; + + Buffers(ForgeConfigSpec.Builder builder) + { + builder.comment(IBuffers.DESC).push(this.getClass().getSimpleName()); + + rebuildTimes = builder + .comment("\n\n" + + IBuffers.REBUILD_TIMES_DESC) + .defineEnum("rebuildFrequency", IBuffers.REBUILD_TIMES_DEFAULT); + + builder.pop(); + } + } + } + } + + + /** {@link Path} to the configuration file of this mod */ + private static final Path CONFIG_PATH = Paths.get("config", ModInfo.NAME + ".toml"); + + public static final ForgeConfigSpec CLIENT_SPEC; + public static final Client CLIENT; + + static + { + final Pair specPair = new ForgeConfigSpec.Builder().configure(Client::new); + CLIENT_SPEC = specPair.getRight(); + CLIENT = specPair.getLeft(); + CommentedFileConfig clientConfig = CommentedFileConfig.builder(CONFIG_PATH) + .writingMode(WritingMode.REPLACE) + .build(); + clientConfig.load(); + clientConfig.save(); + CLIENT_SPEC.setConfig(clientConfig); + } + + @SubscribeEvent + public static void onLoad(final ModConfig.Loading configEvent) + { + LogManager.getLogger().debug(ModInfo.NAME, "Loaded forge config file {}", configEvent.getConfig().getFileName()); + } + + @SubscribeEvent + public static void onFileChange(final ModConfig.Reloading configEvent) + { + LogManager.getLogger().debug(ModInfo.NAME, "Forge config just got changed on the file system!"); + } + +} diff --git a/src/main/java/com/seibel/lod/LodMain.java b/src/main/java/com/seibel/lod/forge/ForgeMain.java similarity index 79% rename from src/main/java/com/seibel/lod/LodMain.java rename to src/main/java/com/seibel/lod/forge/ForgeMain.java index 19e88b0bc..86d9c5ce5 100644 --- a/src/main/java/com/seibel/lod/LodMain.java +++ b/src/main/java/com/seibel/lod/forge/ForgeMain.java @@ -17,10 +17,10 @@ * along with this program. If not, see . */ -package com.seibel.lod; +package com.seibel.lod.forge; -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.proxy.ClientProxy; +import com.seibel.lod.core.ModInfo; +import com.seibel.lod.forge.wrappers.ForgeDependencySetup; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -33,29 +33,27 @@ import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; /** - * Initialize and setup the Mod. - *
+ * Initialize and setup the Mod.
* If you are looking for the real start of the mod * check out the ClientProxy. * * @author James Seibel - * @version 7-3-2021 + * @version 11-16-2021 */ @Mod(ModInfo.ID) -public class LodMain +public class ForgeMain { - public static LodMain instance; - - public static ClientProxy client_proxy; - + public static ForgeClientProxy forgeClientProxy; private void init(final FMLCommonSetupEvent event) { - ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, LodConfig.CLIENT_SPEC); + // make sure the dependencies are set up before the mod needs them + ForgeDependencySetup.createInitialBindings(); + ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ForgeConfig.CLIENT_SPEC); } - public LodMain() + public ForgeMain() { // Register the methods FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init); @@ -67,8 +65,8 @@ public class LodMain private void onClientStart(final FMLClientSetupEvent event) { - client_proxy = new ClientProxy(); - MinecraftForge.EVENT_BUS.register(client_proxy); + forgeClientProxy = new ForgeClientProxy(); + MinecraftForge.EVENT_BUS.register(forgeClientProxy); } diff --git a/src/main/java/com/seibel/lod/mixin/MixinWorldRenderer.java b/src/main/java/com/seibel/lod/forge/mixins/MixinWorldRenderer.java similarity index 74% rename from src/main/java/com/seibel/lod/mixin/MixinWorldRenderer.java rename to src/main/java/com/seibel/lod/forge/mixins/MixinWorldRenderer.java index 23ba25de0..49456ae01 100644 --- a/src/main/java/com/seibel/lod/mixin/MixinWorldRenderer.java +++ b/src/main/java/com/seibel/lod/forge/mixins/MixinWorldRenderer.java @@ -17,15 +17,18 @@ * along with this program. If not, see . */ -package com.seibel.lod.mixin; +package com.seibel.lod.forge.mixins; +import org.lwjgl.opengl.GL15; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.mojang.blaze3d.matrix.MatrixStack; -import com.seibel.lod.LodMain; +import com.seibel.lod.core.api.ClientApi; +import com.seibel.lod.core.objects.math.Mat4f; +import com.seibel.lod.forge.wrappers.McObjectConverter; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.WorldRenderer; @@ -59,6 +62,19 @@ public class MixinWorldRenderer // only render if LODs are enabled and // only render before solid blocks if (renderType.equals(RenderType.solid())) - LodMain.client_proxy.renderLods(matrixStackIn, previousPartialTicks); + { + // get MC's current projection matrix + float[] mcProjMatrixRaw = new float[16]; + GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw); + Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw); + // OpenGl outputs their matrices in col,row form instead of row,col + // (or maybe vice versa I have no idea :P) + mcProjectionMatrix.transpose(); + + + Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose()); + + ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks); + } } } diff --git a/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java b/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java new file mode 100644 index 000000000..f5267f435 --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java @@ -0,0 +1,37 @@ +package com.seibel.lod.forge.wrappers; + +import com.seibel.lod.core.handlers.IReflectionHandler; +import com.seibel.lod.core.handlers.ReflectionHandler; +import com.seibel.lod.core.util.SingletonHandler; +import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; +import com.seibel.lod.forge.wrappers.block.BlockColorSingletonWrapper; +import com.seibel.lod.forge.wrappers.config.LodConfigWrapperSingleton; +import com.seibel.lod.forge.wrappers.minecraft.MinecraftRenderWrapper; +import com.seibel.lod.forge.wrappers.minecraft.MinecraftWrapper; + +/** + * Binds all necessary dependencies so we + * can access them in Core.
+ * This needs to be called before any Core classes + * are loaded. + * + * @author James Seibel + * @version 11-20-2021 + */ +public class ForgeDependencySetup +{ + public static void createInitialBindings() + { + SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE); + SingletonHandler.bind(IBlockColorSingletonWrapper.class, BlockColorSingletonWrapper.INSTANCE); + SingletonHandler.bind(IMinecraftWrapper.class, MinecraftWrapper.INSTANCE); + SingletonHandler.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE); + SingletonHandler.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); + + SingletonHandler.bind(IReflectionHandler.class, ReflectionHandler.createSingleton(MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(), MinecraftWrapper.INSTANCE.getOptions())); + } +} diff --git a/src/main/java/com/seibel/lod/enums/DebugMode.java b/src/main/java/com/seibel/lod/forge/wrappers/McObjectConverter.java similarity index 52% rename from src/main/java/com/seibel/lod/enums/DebugMode.java rename to src/main/java/com/seibel/lod/forge/wrappers/McObjectConverter.java index 1b68f2a6f..d0c08a9ea 100644 --- a/src/main/java/com/seibel/lod/enums/DebugMode.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/McObjectConverter.java @@ -17,38 +17,40 @@ * along with this program. If not, see . */ -package com.seibel.lod.enums; +package com.seibel.lod.forge.wrappers; + +import java.nio.FloatBuffer; + +import com.seibel.lod.core.enums.LodDirection; +import com.seibel.lod.core.objects.math.Mat4f; + +import net.minecraft.util.Direction; +import net.minecraft.util.math.vector.Matrix4f; /** - * off, detail, detail wireframe + * This class converts to and from Minecraft objects (Ex: Matrix4f) + * and objects we created (Ex: Mat4f). * * @author James Seibel - * @version 8-28-2021 + * @version 11-20-2021 */ -public enum DebugMode +public class McObjectConverter { - /** LODs are rendered normally */ - OFF, - - /** LOD colors are based on their detail */ - SHOW_DETAIL, - - /** LOD colors are based on their detail, and draws in wireframe. */ - SHOW_DETAIL_WIREFRAME; - - /** used when cycling through the different modes */ - private DebugMode next; - - static + /** 4x4 float matrix converter */ + public static Mat4f Convert(Matrix4f mcMatrix) { - OFF.next = SHOW_DETAIL; - SHOW_DETAIL.next = SHOW_DETAIL_WIREFRAME; - SHOW_DETAIL_WIREFRAME.next = OFF; + FloatBuffer buffer = FloatBuffer.allocate(16); + mcMatrix.store(buffer); + Mat4f matrix = new Mat4f(buffer); + matrix.transpose(); + return matrix; + } + + + public static Direction Convert(LodDirection lodDirection) + { + return Direction.byName(lodDirection.name()); } - /** returns the next debug mode */ - public DebugMode getNext() - { - return this.next; - } + } diff --git a/src/main/java/com/seibel/lod/forge/wrappers/WrapperFactory.java b/src/main/java/com/seibel/lod/forge/wrappers/WrapperFactory.java new file mode 100644 index 000000000..fa27662ca --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/WrapperFactory.java @@ -0,0 +1,91 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.forge.wrappers; + +import com.seibel.lod.core.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.objects.lod.LodDimension; +import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper; +import com.seibel.lod.forge.wrappers.block.BlockPosWrapper; +import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper; +import com.seibel.lod.forge.wrappers.worldGeneration.WorldGeneratorWrapper; + +/** + * This handles creating abstract wrapper objects. + * + * @author James Seibel + * @version 11-20-2021 + */ +public class WrapperFactory implements IWrapperFactory +{ + public static final WrapperFactory INSTANCE = new WrapperFactory(); + + + @Override + public AbstractBlockPosWrapper createBlockPos() + { + return new BlockPosWrapper(); + } + + @Override + public AbstractBlockPosWrapper createBlockPos(int x, int y, int z) + { + return new BlockPosWrapper(x,y,z); + } + + + + + @Override + public AbstractChunkPosWrapper createChunkPos() + { + return new ChunkPosWrapper(); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(int x, int z) + { + return new ChunkPosWrapper(x, z); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos) + { + return new ChunkPosWrapper(newChunkPos); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos) + { + return new ChunkPosWrapper(blockPos); + } + + + + @Override + public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) + { + return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper); + } + +} diff --git a/src/main/java/com/seibel/lod/enums/FogQuality.java b/src/main/java/com/seibel/lod/forge/wrappers/WrapperUtil.java similarity index 68% rename from src/main/java/com/seibel/lod/enums/FogQuality.java rename to src/main/java/com/seibel/lod/forge/wrappers/WrapperUtil.java index d192a33f4..5f515d71f 100644 --- a/src/main/java/com/seibel/lod/enums/FogQuality.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/WrapperUtil.java @@ -17,17 +17,19 @@ * along with this program. If not, see . */ -package com.seibel.lod.enums; +package com.seibel.lod.forge.wrappers; + +import net.minecraft.world.gen.Heightmap; /** - * fast, fancy, or off + * Stores any variables or code that + * may be shared between wrapper objects. * * @author James Seibel - * @version 02-14-2021 + * @version 11-20-2021 */ -public enum FogQuality +public class WrapperUtil { - FAST, - FANCY, - OFF + /** If we ever need to use a heightmap for any reason, use this one. */ + public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; } diff --git a/src/main/java/com/seibel/lod/util/LodThreadFactory.java b/src/main/java/com/seibel/lod/forge/wrappers/block/BlockColorSingletonWrapper.java similarity index 54% rename from src/main/java/com/seibel/lod/util/LodThreadFactory.java rename to src/main/java/com/seibel/lod/forge/wrappers/block/BlockColorSingletonWrapper.java index 62369e5bd..57eb0dbc4 100644 --- a/src/main/java/com/seibel/lod/util/LodThreadFactory.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/block/BlockColorSingletonWrapper.java @@ -17,30 +17,30 @@ * along with this program. If not, see . */ -package com.seibel.lod.util; +package com.seibel.lod.forge.wrappers.block; + +import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper; + +import net.minecraft.block.Blocks; -import java.util.concurrent.ThreadFactory; /** - * Just a simple ThreadFactory to name ExecutorService - * threads, which can be helpful when debugging. + * Contains methods that would have been static in BlockColorWrapper. + * Since interfaces can't create/implement static methods we have + * to split the object up in two. + * * @author James Seibel - * @version 8-15-2021 + * @version 11-17-2021 */ -public class LodThreadFactory implements ThreadFactory +public class BlockColorSingletonWrapper implements IBlockColorSingletonWrapper { - public final String threadName; - - - public LodThreadFactory(String newThreadName) - { - threadName = newThreadName + " Thread"; - } + public static final BlockColorSingletonWrapper INSTANCE = new BlockColorSingletonWrapper(); @Override - public Thread newThread(Runnable r) + public IBlockColorWrapper getWaterColor() { - return new Thread(r, threadName); + return BlockColorWrapper.getBlockColorWrapper(Blocks.WATER); } - } + diff --git a/src/main/java/com/seibel/lod/wrappers/Block/BlockColorWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/block/BlockColorWrapper.java similarity index 82% rename from src/main/java/com/seibel/lod/wrappers/Block/BlockColorWrapper.java rename to src/main/java/com/seibel/lod/forge/wrappers/block/BlockColorWrapper.java index a45a6da4e..9bc8a9b68 100644 --- a/src/main/java/com/seibel/lod/wrappers/Block/BlockColorWrapper.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/block/BlockColorWrapper.java @@ -1,13 +1,23 @@ -package com.seibel.lod.wrappers.Block; +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -import com.seibel.lod.util.ColorUtil; -import com.seibel.lod.wrappers.MinecraftWrapper; -import net.minecraft.block.*; -import net.minecraft.client.renderer.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import net.minecraftforge.client.model.data.ModelDataMap; +package com.seibel.lod.forge.wrappers.block; import java.util.List; import java.util.Objects; @@ -15,9 +25,33 @@ import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import com.seibel.lod.core.util.ColorUtil; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper; +import com.seibel.lod.forge.wrappers.minecraft.MinecraftWrapper; -//This class wraps the minecraft Block class -public class BlockColorWrapper +import net.minecraft.block.AbstractPlantBlock; +import net.minecraft.block.AbstractTopPlantBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.BushBlock; +import net.minecraft.block.FlowerBlock; +import net.minecraft.block.GrassBlock; +import net.minecraft.block.IGrowable; +import net.minecraft.block.LeavesBlock; +import net.minecraft.block.TallGrassBlock; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraftforge.client.model.data.ModelDataMap; + + +/** + * @author ?? + * @version 11-17-2021 + */ +public class BlockColorWrapper implements IBlockColorWrapper { //set of block which require tint public static final ConcurrentMap blockColorWrapperMap = new ConcurrentHashMap<>(); @@ -61,18 +95,11 @@ public class BlockColorWrapper //System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint); } - /** - * return base color of water (grey value) - */ - static public BlockColorWrapper getWaterColor() - { - return getBlockColorWrapper(Blocks.WATER); - } /** * this return a wrapper of the block in input * @param block object of the block to wrap */ - static public BlockColorWrapper getBlockColorWrapper(Block block) + public static IBlockColorWrapper getBlockColorWrapper(Block block) { //first we check if the block has already been wrapped if (blockColorWrapperMap.containsKey(block) && blockColorWrapperMap.get(block) != null) @@ -134,7 +161,7 @@ public class BlockColorWrapper else { isColored = true; - texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientLevel(), blockPosWrapper.getBlockPos()); + texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientWorld(), blockPosWrapper.getBlockPos()); } int count = 0; @@ -242,11 +269,13 @@ public class BlockColorWrapper //Colors getters// //--------------// + @Override public boolean hasColor() { return isColored; } + @Override public int getColor() { return color; @@ -257,21 +286,25 @@ public class BlockColorWrapper //------------// + @Override public boolean hasTint() { return toTint; } + @Override public boolean hasGrassTint() { return grassTint; } + @Override public boolean hasFolliageTint() { return foliageTint; } + @Override public boolean hasWaterTint() { return waterTint; diff --git a/src/main/java/com/seibel/lod/forge/wrappers/block/BlockPosWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/block/BlockPosWrapper.java new file mode 100644 index 000000000..8939d209e --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/block/BlockPosWrapper.java @@ -0,0 +1,102 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.forge.wrappers.block; + +import java.util.Objects; + +import com.seibel.lod.core.enums.LodDirection; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; + +import net.minecraft.util.math.BlockPos; + +/** + * @author James Seibel + * @version 11-20-2021 + */ +public class BlockPosWrapper extends AbstractBlockPosWrapper +{ + private final BlockPos.Mutable blockPos; + + + public BlockPosWrapper() + { + this.blockPos = new BlockPos.Mutable(0, 0, 0); + } + + public BlockPosWrapper(int x, int y, int z) + { + this.blockPos = new BlockPos.Mutable(x, y, z); + } + + @Override + public void set(int x, int y, int z) + { + blockPos.set(x, y, z); + } + + @Override + public int getX() + { + return blockPos.getX(); + } + + @Override + public int getY() + { + return blockPos.getY(); + } + + @Override + public int getZ() + { + return blockPos.getZ(); + } + + @Override + public int get(LodDirection.Axis axis) + { + return axis.choose(getX(), getY(), getZ()); + } + + public BlockPos.Mutable getBlockPos() + { + return blockPos; + } + + @Override + public boolean equals(Object o) + { + return blockPos.equals(o); + } + + @Override + public int hashCode() + { + return Objects.hash(blockPos); + } + + @Override + public BlockPosWrapper offset(int x, int y, int z) + { + blockPos.set(blockPos.getX() + x, blockPos.getY() + y, blockPos.getZ() + z); + return this; + } + +} diff --git a/src/main/java/com/seibel/lod/wrappers/Block/BlockShapeWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/block/BlockShapeWrapper.java similarity index 70% rename from src/main/java/com/seibel/lod/wrappers/Block/BlockShapeWrapper.java rename to src/main/java/com/seibel/lod/forge/wrappers/block/BlockShapeWrapper.java index 88f17f17d..5a9d71f0b 100644 --- a/src/main/java/com/seibel/lod/wrappers/Block/BlockShapeWrapper.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/block/BlockShapeWrapper.java @@ -1,7 +1,33 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -package com.seibel.lod.wrappers.Block; +package com.seibel.lod.forge.wrappers.block; + +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkWrapper; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.block.SixWayBlock; @@ -10,13 +36,12 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.IBlockReader; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -//This class wraps the minecraft Block class -public class BlockShapeWrapper +/** + * @author ?? + * @version 11-18-2021 + */ +public class BlockShapeWrapper implements IBlockShapeWrapper { //set of block which require tint public static final ConcurrentMap blockShapeWrapperMap = new ConcurrentHashMap<>(); @@ -28,13 +53,13 @@ public class BlockShapeWrapper private boolean noCollision; /**Constructor only require for the block instance we are wrapping**/ - public BlockShapeWrapper(Block block, ChunkWrapper chunkWrapper, BlockPosWrapper blockPosWrapper) + public BlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper) { this.block = block; this.nonFull = false; this.noCollision = false; this.toAvoid = ofBlockToAvoid(); - setupShapes(chunkWrapper, blockPosWrapper); + setupShapes((ChunkWrapper) chunkWrapper, (BlockPosWrapper) blockPosWrapper); //System.out.println(block + " non full " + nonFull + " no collision " + noCollision + " to avoid " + toAvoid); } @@ -50,7 +75,7 @@ public class BlockShapeWrapper * this return a wrapper of the block in input * @param block Block object to wrap */ - static public BlockShapeWrapper getBlockShapeWrapper(Block block, ChunkWrapper chunkWrapper, BlockPosWrapper blockPosWrapper) + static public BlockShapeWrapper getBlockShapeWrapper(Block block, IChunkWrapper chunkWrapper, AbstractBlockPosWrapper blockPosWrapper) { //first we check if the block has already been wrapped if (blockShapeWrapperMap.containsKey(block) && blockShapeWrapperMap.get(block) != null) @@ -103,6 +128,7 @@ public class BlockShapeWrapper } } + @Override public boolean ofBlockToAvoid() { return block.equals(Blocks.AIR) @@ -115,16 +141,19 @@ public class BlockShapeWrapper //-----------------// + @Override public boolean isNonFull() { return nonFull; } + @Override public boolean hasNoCollision() { return noCollision; } + @Override public boolean isToAvoid() { return toAvoid; diff --git a/src/main/java/com/seibel/lod/forge/wrappers/chunk/ChunkPosWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/chunk/ChunkPosWrapper.java new file mode 100644 index 000000000..1442e8307 --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/chunk/ChunkPosWrapper.java @@ -0,0 +1,135 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.forge.wrappers.chunk; + +import java.util.Objects; + +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +import com.seibel.lod.forge.wrappers.block.BlockPosWrapper; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; + + +/** + * @author James Seibel + * @version 11-18-2021 + */ +public class ChunkPosWrapper extends AbstractChunkPosWrapper +{ + private final ChunkPos chunkPos; + + public ChunkPosWrapper(ChunkPos newChunkPos) + { + this.chunkPos = newChunkPos; + } + + public ChunkPosWrapper(BlockPos blockPos) + { + this.chunkPos = new ChunkPos(blockPos); + } + + + public ChunkPosWrapper(AbstractChunkPosWrapper newChunkPos) + { + this.chunkPos = ((ChunkPosWrapper) newChunkPos).chunkPos; + } + + public ChunkPosWrapper(AbstractBlockPosWrapper blockPos) + { + this.chunkPos = new ChunkPos(((BlockPosWrapper) blockPos).getBlockPos()); + } + + public ChunkPosWrapper(int chunkX, int chunkZ) + { + this.chunkPos = new ChunkPos(chunkX, chunkZ); + } + + public ChunkPosWrapper() + { + this.chunkPos = new ChunkPos(0, 0); + } + + + + @Override + public int getX() + { + return chunkPos.x; + } + + @Override + public int getZ() + { + return chunkPos.z; + } + + @Override + public int getMinBlockX() + { + return chunkPos.getMinBlockX(); + } + + @Override + public int getMinBlockZ() + { + return chunkPos.getMinBlockZ(); + } + + @Override + public int getRegionX() + { + return chunkPos.getRegionX(); + } + + @Override + public int getRegionZ() + { + return chunkPos.getRegionZ(); + } + + public ChunkPos getChunkPos() + { + return chunkPos; + } + + + + @Override + public boolean equals(Object o) + { + return chunkPos.equals(o); + } + + @Override + public int hashCode() + { + return Objects.hash(chunkPos); + } + + @Override + public BlockPosWrapper getWorldPosition() + { + BlockPos blockPos = chunkPos.getWorldPosition(); + return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + } + +} diff --git a/src/main/java/com/seibel/lod/forge/wrappers/chunk/ChunkWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/chunk/ChunkWrapper.java new file mode 100644 index 000000000..cebaf940d --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/chunk/ChunkWrapper.java @@ -0,0 +1,125 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.forge.wrappers.chunk; + +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper; +import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.lod.forge.wrappers.WrapperUtil; +import com.seibel.lod.forge.wrappers.block.BlockColorWrapper; +import com.seibel.lod.forge.wrappers.block.BlockPosWrapper; +import com.seibel.lod.forge.wrappers.block.BlockShapeWrapper; +import com.seibel.lod.forge.wrappers.world.BiomeWrapper; + +import net.minecraft.block.BlockState; +import net.minecraft.block.ILiquidContainer; +import net.minecraft.block.IWaterLoggable; +import net.minecraft.state.properties.BlockStateProperties; +import net.minecraft.world.chunk.IChunk; + +/** + * @author ?? + * @version 11-17-2021 + */ +public class ChunkWrapper implements IChunkWrapper +{ + private final IChunk chunk; + private final ChunkPosWrapper chunkPos; + + @Override + public int getHeight() + { + return chunk.getMaxBuildHeight(); + } + + @Override + public boolean isPositionInWater(AbstractBlockPosWrapper blockPos) + { + BlockState blockState = chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()); + + //This type of block is always in water + return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable)) + || (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)); + } + + @Override + public int getHeightMapValue(int xRel, int zRel) + { + return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel); + } + + @Override + public BiomeWrapper getBiome(int xRel, int yAbs, int zRel) + { + return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2)); + } + + @Override + public IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos) + { + return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock()); + } + + @Override + public IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos) + { + return BlockShapeWrapper.getBlockShapeWrapper(chunk.getBlockState(((BlockPosWrapper) blockPos).getBlockPos()).getBlock(), this, blockPos); + } + + public ChunkWrapper(IChunk chunk) + { + this.chunk = chunk; + this.chunkPos = new ChunkPosWrapper(chunk.getPos()); + } + + public IChunk getChunk() + { + return chunk; + } + + @Override + public ChunkPosWrapper getPos() + { + return chunkPos; + } + + @Override + public boolean isLightCorrect() + { + return chunk.isLightCorrect(); + } + + @Override + public boolean isWaterLogged(AbstractBlockPosWrapper blockPos) + { + BlockState blockState = chunk.getBlockState(((BlockPosWrapper)blockPos).getBlockPos()); + + //This type of block is always in water + return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable)) + || (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)); + } + + @Override + public int getEmittedBrightness(AbstractBlockPosWrapper blockPos) + { + return chunk.getLightEmission(((BlockPosWrapper)blockPos).getBlockPos()); + } +} diff --git a/src/main/java/com/seibel/lod/forge/wrappers/config/LodConfigWrapperSingleton.java b/src/main/java/com/seibel/lod/forge/wrappers/config/LodConfigWrapperSingleton.java new file mode 100644 index 000000000..ebbe7c444 --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/config/LodConfigWrapperSingleton.java @@ -0,0 +1,491 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.forge.wrappers.config; + +import com.seibel.lod.core.enums.config.BlocksToAvoid; +import com.seibel.lod.core.enums.config.BufferRebuildTimes; +import com.seibel.lod.core.enums.config.DistanceGenerationMode; +import com.seibel.lod.core.enums.config.GenerationPriority; +import com.seibel.lod.core.enums.config.GpuUploadMethod; +import com.seibel.lod.core.enums.config.HorizontalQuality; +import com.seibel.lod.core.enums.config.HorizontalResolution; +import com.seibel.lod.core.enums.config.HorizontalScale; +import com.seibel.lod.core.enums.config.LodTemplate; +import com.seibel.lod.core.enums.config.VanillaOverdraw; +import com.seibel.lod.core.enums.config.VerticalQuality; +import com.seibel.lod.core.enums.rendering.DebugMode; +import com.seibel.lod.core.enums.rendering.FogDistance; +import com.seibel.lod.core.enums.rendering.FogDrawOverride; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; +import com.seibel.lod.forge.ForgeConfig; + +/** + * @author James Seibel + * @version 11-16-2021 + */ +public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton +{ + public static final LodConfigWrapperSingleton INSTANCE = new LodConfigWrapperSingleton(); + + + private static final Client client = new Client(); + @Override + public IClient client() + { + return client; + } + + public static class Client implements IClient + { + public final IGraphics graphics; + public final IWorldGenerator worldGenerator; + public final IAdvanced advanced; + + + @Override + public IGraphics graphics() + { + return graphics; + } + + @Override + public IWorldGenerator worldGenerator() + { + return worldGenerator; + } + + @Override + public IAdvanced advanced() + { + return advanced; + } + + + + //================// + // Client Configs // + //================// + public Client() + { + graphics = new Graphics(); + worldGenerator = new WorldGenerator(); + advanced = new Advanced(); + } + + + //==================// + // Graphics Configs // + //==================// + public static class Graphics implements IGraphics + { + public final IQuality quality; + public final IFogQuality fogQuality; + public final IAdvancedGraphics advancedGraphics; + + + + @Override + public IQuality quality() + { + return quality; + } + + @Override + public IFogQuality fogQuality() + { + return fogQuality; + } + + @Override + public IAdvancedGraphics advancedGraphics() + { + return advancedGraphics; + } + + + Graphics() + { + quality = new Quality(); + advancedGraphics = new AdvancedGraphics(); + fogQuality = new FogQuality(); + } + + + public static class Quality implements IQuality + { + @Override + public HorizontalResolution getDrawResolution() + { + return ForgeConfig.CLIENT.graphics.qualityOption.drawResolution.get(); + } + @Override + public void setDrawResolution(HorizontalResolution newHorizontalResolution) + { + ForgeConfig.CLIENT.graphics.qualityOption.drawResolution.set(newHorizontalResolution); + } + + + @Override + public int getLodChunkRenderDistance() + { + return ForgeConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(); + } + @Override + public void setLodChunkRenderDistance(int newLodChunkRenderDistance) + { + ForgeConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(newLodChunkRenderDistance); + } + + + @Override + public VerticalQuality getVerticalQuality() + { + return ForgeConfig.CLIENT.graphics.qualityOption.verticalQuality.get(); + } + @Override + public void setVerticalQuality(VerticalQuality newVerticalQuality) + { + ForgeConfig.CLIENT.graphics.qualityOption.verticalQuality.set(newVerticalQuality); + } + + + @Override + public HorizontalScale getHorizontalScale() + { + return ForgeConfig.CLIENT.graphics.qualityOption.horizontalScale.get(); + } + @Override + public void setHorizontalScale(HorizontalScale newHorizontalScale) + { + ForgeConfig.CLIENT.graphics.qualityOption.horizontalScale.set(newHorizontalScale); + } + + + @Override + public HorizontalQuality getHorizontalQuality() + { + return ForgeConfig.CLIENT.graphics.qualityOption.horizontalQuality.get(); + } + @Override + public void setHorizontalQuality(HorizontalQuality newHorizontalQuality) + { + ForgeConfig.CLIENT.graphics.qualityOption.horizontalQuality.set(newHorizontalQuality); + } + } + + + public static class FogQuality implements IFogQuality + { + @Override + public FogDistance getFogDistance() + { + return ForgeConfig.CLIENT.graphics.fogQuality.fogDistance.get(); + } + @Override + public void setFogDistance(FogDistance newFogDistance) + { + ForgeConfig.CLIENT.graphics.fogQuality.fogDistance.set(newFogDistance); + } + + + @Override + public FogDrawOverride getFogDrawOverride() + { + return ForgeConfig.CLIENT.graphics.fogQuality.fogDrawOverride.get(); + } + @Override + public void setFogDrawOverride(FogDrawOverride newFogDrawOverride) + { + ForgeConfig.CLIENT.graphics.fogQuality.fogDrawOverride.set(newFogDrawOverride); + } + + + @Override + public boolean getDisableVanillaFog() + { + return ForgeConfig.CLIENT.graphics.fogQuality.disableVanillaFog.get(); + } + @Override + public void setDisableVanillaFog(boolean newDisableVanillaFog) + { + ForgeConfig.CLIENT.graphics.fogQuality.disableVanillaFog.set(newDisableVanillaFog); + } + } + + + public static class AdvancedGraphics implements IAdvancedGraphics + { + @Override + public LodTemplate getLodTemplate() + { + return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.get(); + } + @Override + public void setLodTemplate(LodTemplate newLodTemplate) + { + ForgeConfig.CLIENT.graphics.advancedGraphicsOption.lodTemplate.set(newLodTemplate); + } + + + @Override + public boolean getDisableDirectionalCulling() + { + return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get(); + } + @Override + public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling) + { + ForgeConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.set(newDisableDirectionalCulling); + } + + + @Override + public boolean getAlwaysDrawAtMaxQuality() + { + return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.get(); + } + @Override + public void setAlwaysDrawAtMaxQuality(boolean newAlwaysDrawAtMaxQuality) + { + ForgeConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.set(newAlwaysDrawAtMaxQuality); + } + + + @Override + public VanillaOverdraw getVanillaOverdraw() + { + return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get(); + } + @Override + public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw) + { + ForgeConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.set(newVanillaOverdraw); + } + + + @Override + public GpuUploadMethod getGpuUploadMethod() + { + return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get(); + } + @Override + public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog) + { + ForgeConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(newDisableVanillaFog); + } + + + @Override + public boolean getUseExtendedNearClipPlane() + { + return ForgeConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get(); + } + @Override + public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane) + { + ForgeConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.set(newUseExtendedNearClipPlane); + } + } + } + + + + + //========================// + // WorldGenerator Configs // + //========================// + public static class WorldGenerator implements IWorldGenerator + { + @Override + public GenerationPriority getGenerationPriority() + { + return ForgeConfig.CLIENT.worldGenerator.generationPriority.get(); + } + @Override + public void setGenerationPriority(GenerationPriority newGenerationPriority) + { + ForgeConfig.CLIENT.worldGenerator.generationPriority.set(newGenerationPriority); + } + + + @Override + public DistanceGenerationMode getDistanceGenerationMode() + { + return ForgeConfig.CLIENT.worldGenerator.distanceGenerationMode.get(); + } + @Override + public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode) + { + ForgeConfig.CLIENT.worldGenerator.distanceGenerationMode.set(newDistanceGenerationMode); + } + + + @Override + public boolean getAllowUnstableFeatureGeneration() + { + return ForgeConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.get(); + } + @Override + public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration) + { + ForgeConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(newAllowUnstableFeatureGeneration); + } + + + @Override + public BlocksToAvoid getBlocksToAvoid() + { + return ForgeConfig.CLIENT.worldGenerator.blocksToAvoid.get(); + } + @Override + public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid) + { + ForgeConfig.CLIENT.worldGenerator.blocksToAvoid.set(newBlockToAvoid); + } + } + + + + + //============================// + // AdvancedModOptions Configs // + //============================// + public static class Advanced implements IAdvanced + { + public final IThreading threading; + public final IDebugging debugging; + public final IBuffers buffers; + + + @Override + public IThreading threading() + { + return threading; + } + + + @Override + public IDebugging debugging() + { + return debugging; + } + + + @Override + public IBuffers buffers() + { + return buffers; + } + + + public Advanced() + { + threading = new Threading(); + debugging = new Debugging(); + buffers = new Buffers(); + } + + public static class Threading implements IThreading + { + @Override + public int getNumberOfWorldGenerationThreads() + { + return ForgeConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.get(); + } + @Override + public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads) + { + ForgeConfig.CLIENT.advancedModOptions.threading.numberOfWorldGenerationThreads.set(newNumberOfWorldGenerationThreads); + } + + + @Override + public int getNumberOfBufferBuilderThreads() + { + return ForgeConfig.CLIENT.advancedModOptions.threading.numberOfBufferBuilderThreads.get(); + } + @Override + public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads) + { + ForgeConfig.CLIENT.advancedModOptions.threading.numberOfBufferBuilderThreads.set(newNumberOfWorldBuilderThreads); + } + } + + + + + //===============// + // Debug Options // + //===============// + public static class Debugging implements IDebugging + { + @Override + public boolean getDrawLods() + { + return ForgeConfig.CLIENT.advancedModOptions.debugging.drawLods.get(); + } + @Override + public void setDrawLods(boolean newDrawLods) + { + ForgeConfig.CLIENT.advancedModOptions.debugging.drawLods.set(newDrawLods); + } + + + @Override + public DebugMode getDebugMode() + { + return ForgeConfig.CLIENT.advancedModOptions.debugging.debugMode.get(); + } + @Override + public void setDebugMode(DebugMode newDebugMode) + { + ForgeConfig.CLIENT.advancedModOptions.debugging.debugMode.set(newDebugMode); + } + + + @Override + public boolean getDebugKeybindingsEnabled() + { + return ForgeConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.get(); + } + @Override + public void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings) + { + ForgeConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.set(newEnableDebugKeybindings); + } + } + + + public static class Buffers implements IBuffers + { + @Override + public BufferRebuildTimes getRebuildTimes() + { + return ForgeConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get(); + } + @Override + public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes) + { + ForgeConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.set(newBufferRebuildTimes); + } + } + } + } +} diff --git a/src/main/java/com/seibel/lod/forge/wrappers/minecraft/MinecraftRenderWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/minecraft/MinecraftRenderWrapper.java new file mode 100644 index 000000000..2e5737ee0 --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/minecraft/MinecraftRenderWrapper.java @@ -0,0 +1,139 @@ +package com.seibel.lod.forge.wrappers.minecraft; + +import java.util.HashSet; + +import com.seibel.lod.core.objects.math.Mat4f; +import com.seibel.lod.core.objects.math.Vec3d; +import com.seibel.lod.core.objects.math.Vec3f; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.lod.forge.wrappers.McObjectConverter; +import com.seibel.lod.forge.wrappers.block.BlockPosWrapper; +import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ActiveRenderInfo; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk; +import net.minecraft.potion.Effects; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.util.math.vector.Vector3f; + +/** + * A singleton that contains everything + * related to rendering in Minecraft. + * + * @author James Seibel + * @version 11-18-2021 + */ +public class MinecraftRenderWrapper implements IMinecraftRenderWrapper +{ + public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper(); + + private final GameRenderer gameRenderer = Minecraft.getInstance().gameRenderer; + private final static Minecraft mc = Minecraft.getInstance(); + + + + @Override + public Vec3f getLookAtVector() + { + ActiveRenderInfo camera = gameRenderer.getMainCamera(); + Vector3f cameraDir = camera.getLookVector(); + return new Vec3f(cameraDir.x(), cameraDir.y(), cameraDir.z()); + } + + @Override + public AbstractBlockPosWrapper getCameraBlockPosition() + { + ActiveRenderInfo camera = gameRenderer.getMainCamera(); + BlockPos blockPos = camera.getBlockPosition(); + return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + } + + @Override + public boolean playerHasBlindnessEffect() + { + return mc.player.getActiveEffectsMap().get(Effects.BLINDNESS) != null; + } + + @Override + public Vec3d getCameraExactPosition() + { + ActiveRenderInfo camera = gameRenderer.getMainCamera(); + Vector3d projectedView = camera.getPosition(); + + return new Vec3d(projectedView.x, projectedView.y, projectedView.z); + } + + @Override + public Mat4f getDefaultProjectionMatrix(float partialTicks) + { + return McObjectConverter.Convert(gameRenderer.getProjectionMatrix(gameRenderer.getMainCamera(), partialTicks, true)); + } + + @Override + public double getGamma() + { + return mc.options.gamma; + } + + @Override + public double getFov(float partialTicks) + { + return gameRenderer.getFov(gameRenderer.getMainCamera(), partialTicks, true); + } + + /** Measured in chunks */ + @Override + public int getRenderDistance() + { + return mc.options.renderDistance; + } + + @Override + public int getScreenWidth() + { + return mc.getWindow().getWidth(); + } + @Override + public int getScreenHeight() + { + return mc.getWindow().getHeight(); + } + + /** + * This method returns the ChunkPos of all chunks that Minecraft + * is going to render this frame.

+ *

+ * Note: This isn't perfect. It will return some chunks that are outside + * the clipping plane. (For example, if you are high above the ground some chunks + * will be incorrectly added, even though they are outside render range). + */ + @Override + public HashSet getRenderedChunks() + { + HashSet loadedPos = new HashSet<>(); + + // Wow, those are some long names! + + // go through every RenderInfo to get the compiled chunks + WorldRenderer renderer = mc.levelRenderer; + for (WorldRenderer.LocalRenderInformationContainer worldRenderer$LocalRenderInformationContainer : renderer.renderChunks) + { + CompiledChunk compiledChunk = worldRenderer$LocalRenderInformationContainer.chunk.getCompiledChunk(); + if (!compiledChunk.hasNoRenderableLayers()) + { + // add the ChunkPos for every rendered chunk + BlockPos bpos = worldRenderer$LocalRenderInformationContainer.chunk.getOrigin(); + + loadedPos.add(new ChunkPosWrapper(bpos)); + } + } + + return loadedPos; + } +} diff --git a/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/minecraft/MinecraftWrapper.java similarity index 60% rename from src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java rename to src/main/java/com/seibel/lod/forge/wrappers/minecraft/MinecraftWrapper.java index 70bfb4d23..ce4bec53c 100644 --- a/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/minecraft/MinecraftWrapper.java @@ -17,18 +17,28 @@ * along with this program. If not, see . */ -package com.seibel.lod.wrappers; +package com.seibel.lod.forge.wrappers.minecraft; import java.awt.Color; import java.io.File; +import java.util.ArrayList; -import com.seibel.lod.ModInfo; -import com.seibel.lod.proxy.ClientProxy; -import com.seibel.lod.util.LodUtil; +import com.seibel.lod.core.ModInfo; +import com.seibel.lod.core.api.ClientApi; +import com.seibel.lod.core.enums.LodDirection; +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; +import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.forge.wrappers.McObjectConverter; +import com.seibel.lod.forge.wrappers.block.BlockPosWrapper; +import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper; +import com.seibel.lod.forge.wrappers.misc.LightMapWrapper; +import com.seibel.lod.forge.wrappers.world.DimensionTypeWrapper; +import com.seibel.lod.forge.wrappers.world.WorldWrapper; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; -import com.seibel.lod.wrappers.World.LevelWrapper; import net.minecraft.client.GameSettings; import net.minecraft.client.MainWindow; import net.minecraft.client.Minecraft; @@ -43,10 +53,10 @@ import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.world.ClientWorld; import net.minecraft.crash.CrashReport; import net.minecraft.entity.Entity; -import net.minecraft.profiler.IProfiler; import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.StringTextComponent; import net.minecraft.world.DimensionType; import net.minecraft.world.server.ServerWorld; @@ -57,7 +67,7 @@ import net.minecraft.world.server.ServerWorld; * @author James Seibel * @version 9-16-2021 */ -public class MinecraftWrapper +public class MinecraftWrapper implements IMinecraftWrapper { public static final MinecraftWrapper INSTANCE = new MinecraftWrapper(); @@ -69,6 +79,9 @@ public class MinecraftWrapper */ private NativeImage lightMap = null; + private ProfilerWrapper profilerWrapper; + + private MinecraftWrapper() { @@ -88,6 +101,7 @@ public class MinecraftWrapper *

* This doesn't affect OpenGL objects in any way. */ + @Override public void clearFrameObjectCache() { lightMap = null; @@ -99,30 +113,53 @@ public class MinecraftWrapper // method wrappers // //=================// - public float getShading(Direction direction) + @Override + public float getShade(LodDirection lodDirection) { - return mc.level.getShade(Direction.UP, true); + Direction mcDir = McObjectConverter.Convert(lodDirection); + return mc.level.getShade(mcDir, true); } + @Override public boolean hasSinglePlayerServer() { return mc.hasSingleplayerServer(); } - public DimensionType getCurrentDimension() + @Override + public String getCurrentServerName() { - return mc.player.level.dimensionType(); + return mc.getCurrentServer().name; } + @Override + public String getCurrentServerIp() + { + return mc.getCurrentServer().ip; + } + + @Override + public String getCurrentServerVersion() + { + return mc.getCurrentServer().version.getString(); + } + + /** Returns the dimension the player is currently in */ + @Override + public IDimensionTypeWrapper getCurrentDimension() + { + return DimensionTypeWrapper.getDimensionTypeWrapper(mc.player.level.dimensionType()); + } + + @Override public String getCurrentDimensionId() { - return LodUtil.getDimensionIDFromWorld(mc.level); + return LodUtil.getDimensionIDFromWorld(WorldWrapper.getWorldWrapper(mc.level)); } - /** - * This texture changes every frame - */ - public NativeImage getCurrentLightMap() + /** This texture changes every frame */ + @Override + public ILightMapWrapper getCurrentLightMap() { // get the current lightMap if the cache is empty if (lightMap == null) @@ -130,7 +167,7 @@ public class MinecraftWrapper LightTexture tex = mc.gameRenderer.lightTexture(); lightMap = tex.lightPixels; } - return lightMap; + return new LightMapWrapper(lightMap); } /** @@ -139,6 +176,7 @@ public class MinecraftWrapper * @param u x location in texture space * @param v z location in texture space */ + @Override public int getColorIntFromLightMap(int u, int v) { if (lightMap == null) @@ -156,6 +194,7 @@ public class MinecraftWrapper * @param u x location in texture space * @param v z location in texture space */ + @Override public Color getColorFromLightMap(int u, int v) { return LodUtil.intToColor(lightMap.getPixelRGBA(u, v)); @@ -173,17 +212,25 @@ public class MinecraftWrapper return mc.player; } + @Override + public boolean playerExists() + { + return mc.player != null; + } + + @Override public BlockPosWrapper getPlayerBlockPos() { BlockPos playerPos = getPlayer().blockPosition(); return new BlockPosWrapper(playerPos.getX(), playerPos.getY(), playerPos.getZ()); } - + + @Override public ChunkPosWrapper getPlayerChunkPos() { return new ChunkPosWrapper(getPlayer().xChunk, getPlayer().zChunk); } - + public GameSettings getOptions() { return mc.options; @@ -194,55 +241,62 @@ public class MinecraftWrapper return mc.getModelManager(); } - public ClientWorld getClientLevel() + public ClientWorld getClientWorld() { return mc.level; } - public LevelWrapper getWrappedClientLevel() + /** + * Attempts to get the ServerWorld for the dimension + * the user is currently in. + * @returns null if no ServerWorld is available + */ + @Override + public IWorldWrapper getWrappedServerWorld() { - return LevelWrapper.getLevelWrapper(mc.level); - } - - public LevelWrapper getWrappedServerLevel() - { - if (mc.level == null) return null; + DimensionType dimension = mc.level.dimensionType(); IntegratedServer server = mc.getSingleplayerServer(); + if (server == null) return null; + ServerWorld serverWorld = null; Iterable worlds = server.getAllLevels(); - ServerWorld returnWorld = null; - for (ServerWorld world : worlds) { if (world.dimensionType() == dimension) { - returnWorld = world; + serverWorld = world; break; } } - - return LevelWrapper.getLevelWrapper(returnWorld); + return WorldWrapper.getWorldWrapper(serverWorld); } - /** Measured in chunks */ - public int getRenderDistance() + @Override + public IWorldWrapper getWrappedClientWorld() { - return mc.options.renderDistance; + return WorldWrapper.getWorldWrapper(mc.level); } + @Override public File getGameDirectory() { return mc.gameDirectory; } - public IProfiler getProfiler() + @Override + public IProfilerWrapper getProfiler() { - return mc.getProfiler(); + if (profilerWrapper == null) + profilerWrapper = new ProfilerWrapper(mc.getProfiler()); + else if (mc.getProfiler() != profilerWrapper.profiler) + profilerWrapper.profiler = mc.getProfiler(); + + return profilerWrapper; } public ClientPlayNetHandler getConnection() @@ -265,6 +319,7 @@ public class MinecraftWrapper return mc.getWindow(); } + @Override public float getSkyDarken(float partialTicks) { return mc.level.getSkyDarken(partialTicks); @@ -275,6 +330,12 @@ public class MinecraftWrapper return mc.getSingleplayerServer(); } + @Override + public boolean connectedToServer() + { + return mc.getCurrentServer() != null; + } + public ServerData getCurrentServer() { return mc.getCurrentServer(); @@ -285,19 +346,49 @@ public class MinecraftWrapper return mc.levelRenderer; } + /** Returns all worlds available to the server */ + @Override + public ArrayList getAllServerWorlds() + { + ArrayList worlds = new ArrayList(); + + Iterable serverWorlds = mc.getSingleplayerServer().getAllLevels(); + for (ServerWorld world : serverWorlds) + { + worlds.add(WorldWrapper.getWorldWrapper(world)); + } + + return worlds; + } + + + + @Override + public void sendChatMessage(String string) + { + getPlayer().sendMessage(new StringTextComponent(string), getPlayer().getUUID()); + } /** * Crashes Minecraft, displaying the given errorMessage

* In the following format:
* * The game crashed whilst errorMessage
- * Error: java.lang.ExceptionClass: exceptionErrorMessage
+ * Error: ExceptionClass: exceptionErrorMessage
* Exit Code: -1
*/ + @Override public void crashMinecraft(String errorMessage, Throwable exception) { - ClientProxy.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft..."); + ClientApi.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft..."); CrashReport report = new CrashReport(errorMessage, exception); Minecraft.crash(report); } + + + + + + + } diff --git a/src/main/java/com/seibel/lod/forge/wrappers/minecraft/ProfilerWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/minecraft/ProfilerWrapper.java new file mode 100644 index 000000000..78c9f4a7e --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/minecraft/ProfilerWrapper.java @@ -0,0 +1,43 @@ +package com.seibel.lod.forge.wrappers.minecraft; + +import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper; + +import net.minecraft.profiler.IProfiler; + +/** + * + * + * @author James Seibel + * @version 11-20-2021 + */ +public class ProfilerWrapper implements IProfilerWrapper +{ + public IProfiler profiler; + + public ProfilerWrapper(IProfiler newProfiler) + { + profiler = newProfiler; + } + + + /** starts a new section inside the currently running section */ + @Override + public void push(String newSection) + { + profiler.push(newSection); + } + + /** ends the currently running section and starts a new one */ + @Override + public void popPush(String newSection) + { + profiler.popPush(newSection); + } + + /** ends the currently running section */ + @Override + public void pop() + { + profiler.pop(); + } +} diff --git a/src/main/java/com/seibel/lod/forge/wrappers/misc/LightMapWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/misc/LightMapWrapper.java new file mode 100644 index 000000000..a5181b9e5 --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/misc/LightMapWrapper.java @@ -0,0 +1,31 @@ +package com.seibel.lod.forge.wrappers.misc; + +import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper; + +import net.minecraft.client.renderer.texture.NativeImage; + +/** + * + * @author Leonardo Amato + * @version 11-13-2021 + */ +public class LightMapWrapper implements ILightMapWrapper +{ + private NativeImage lightMap = null; + + public LightMapWrapper(NativeImage newlightMap) + { + lightMap = newlightMap; + } + + public void setLightMap(NativeImage newlightMap) + { + lightMap = newlightMap; + } + + @Override + public int getLightValue(int skyLight, int blockLight) + { + return lightMap.getPixelRGBA(skyLight, blockLight); + } +} diff --git a/src/main/java/com/seibel/lod/forge/wrappers/world/BiomeColorWrapperSingleton.java b/src/main/java/com/seibel/lod/forge/wrappers/world/BiomeColorWrapperSingleton.java new file mode 100644 index 000000000..316b9c4b9 --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/world/BiomeColorWrapperSingleton.java @@ -0,0 +1,60 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.forge.wrappers.world; + +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IBiomeColorWrapperSingleton; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.forge.wrappers.block.BlockPosWrapper; + +import net.minecraft.world.biome.BiomeColors; + + +/** + * @author Cola? + * @version 11-15-2021 + */ +public class BiomeColorWrapperSingleton implements IBiomeColorWrapperSingleton +{ + private static final BiomeColorWrapperSingleton instance = new BiomeColorWrapperSingleton(); + + @Override + public IBiomeColorWrapperSingleton getInstance() + { + return instance; + } + + + @Override + public int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos) + { + return BiomeColors.getAverageGrassColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos()); + } + @Override + public int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos) + { + return BiomeColors.getAverageWaterColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos()); + } + @Override + public int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos) + { + return BiomeColors.getAverageFoliageColor(((WorldWrapper)world).getWorld(), ((BlockPosWrapper) blockPos).getBlockPos()); + } +} diff --git a/src/main/java/com/seibel/lod/wrappers/World/BiomeWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/world/BiomeWrapper.java similarity index 86% rename from src/main/java/com/seibel/lod/wrappers/World/BiomeWrapper.java rename to src/main/java/com/seibel/lod/forge/wrappers/world/BiomeWrapper.java index 8e513a848..455af70af 100644 --- a/src/main/java/com/seibel/lod/wrappers/World/BiomeWrapper.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/world/BiomeWrapper.java @@ -1,19 +1,24 @@ -package com.seibel.lod.wrappers.World; - -import com.seibel.lod.util.ColorUtil; -import com.seibel.lod.wrappers.Block.BlockColorWrapper; -import net.minecraft.block.Blocks; -import net.minecraft.world.biome.Biome; +package com.seibel.lod.forge.wrappers.world; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import com.seibel.lod.core.util.ColorUtil; +import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; +import com.seibel.lod.forge.wrappers.block.BlockColorSingletonWrapper; +import com.seibel.lod.forge.wrappers.block.BlockColorWrapper; -//This class wraps the minecraft BlockPos.Mutable (and BlockPos) class -public class BiomeWrapper +import net.minecraft.block.Blocks; +import net.minecraft.world.biome.Biome; + + +/** + * @author James Seibel + * @version 11-15-2021 + */ +public class BiomeWrapper implements IBiomeWrapper { - public static final ConcurrentMap biomeWrapperMap = new ConcurrentHashMap<>(); private final Biome biome; @@ -38,7 +43,9 @@ public class BiomeWrapper } + /** Returns a color int for the given biome. */ + @Override public int getColorForBiome(int x, int z) { int colorInt; @@ -78,7 +85,7 @@ public class BiomeWrapper case OCEAN: case RIVER: - colorInt = BlockColorWrapper.getWaterColor().getColor(); + colorInt = BlockColorSingletonWrapper.INSTANCE.getWaterColor().getColor(); tintValue = biome.getWaterColor(); break; @@ -113,16 +120,19 @@ public class BiomeWrapper return colorInt; } + @Override public int getGrassTint(int x, int z) { return biome.getGrassColor(x, z); } + @Override public int getFolliageTint() { return biome.getFoliageColor(); } + @Override public int getWaterTint() { return biome.getWaterColor(); diff --git a/src/main/java/com/seibel/lod/wrappers/World/DimensionTypeWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/world/DimensionTypeWrapper.java similarity index 67% rename from src/main/java/com/seibel/lod/wrappers/World/DimensionTypeWrapper.java rename to src/main/java/com/seibel/lod/forge/wrappers/world/DimensionTypeWrapper.java index 32ee894b1..13cdd0fd7 100644 --- a/src/main/java/com/seibel/lod/wrappers/World/DimensionTypeWrapper.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/world/DimensionTypeWrapper.java @@ -1,11 +1,17 @@ -package com.seibel.lod.wrappers.World; - -import net.minecraft.world.DimensionType; +package com.seibel.lod.forge.wrappers.world; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -public class DimensionTypeWrapper +import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; + +import net.minecraft.world.DimensionType; + +/** + * @author ?? + * @version 11-15-2021 + */ +public class DimensionTypeWrapper implements IDimensionTypeWrapper { private static final ConcurrentMap dimensionTypeWrapperMap = new ConcurrentHashMap<>(); private final DimensionType dimensionType; @@ -30,8 +36,21 @@ public class DimensionTypeWrapper return dimensionTypeWrapper; } - public static void clearMap() + @Override + public String getDimensionName() { - dimensionTypeWrapperMap.clear(); + return dimensionType.effectsLocation().getPath(); + } + + @Override + public boolean hasCeiling() + { + return dimensionType.hasCeiling(); + } + + @Override + public boolean hasSkyLight() + { + return dimensionType.hasSkyLight(); } } diff --git a/src/main/java/com/seibel/lod/forge/wrappers/world/WorldWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/world/WorldWrapper.java new file mode 100644 index 000000000..15dbdfa0e --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/world/WorldWrapper.java @@ -0,0 +1,151 @@ +package com.seibel.lod.forge.wrappers.world; + +import java.io.File; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.seibel.lod.core.enums.WorldType; +import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.forge.wrappers.block.BlockPosWrapper; + +import net.minecraft.client.world.ClientWorld; +import net.minecraft.world.IWorld; +import net.minecraft.world.server.ServerChunkProvider; +import net.minecraft.world.server.ServerWorld; + +/** + * @author James Seibel + * @author ?? + * @version 11-20-2021 + */ +public class WorldWrapper implements IWorldWrapper +{ + private static final ConcurrentMap worldWrapperMap = new ConcurrentHashMap<>(); + private final IWorld world; + public final WorldType worldType; + + + public WorldWrapper(IWorld newWorld) + { + world = newWorld; + + if (world.getClass() == ServerWorld.class) + worldType = WorldType.ServerWorld; + else if (world.getClass() == ClientWorld.class) + worldType = WorldType.ClientWorld; + else + worldType = WorldType.Unknown; + } + + + + public static WorldWrapper getWorldWrapper(IWorld world) + { + //first we check if the biome has already been wrapped + if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null) + return worldWrapperMap.get(world); + + + //if it hasn't been created yet, we create it and save it in the map + WorldWrapper worldWrapper = new WorldWrapper(world); + worldWrapperMap.put(world, worldWrapper); + + //we return the newly created wrapper + return worldWrapper; + } + + public static void clearMap() + { + worldWrapperMap.clear(); + } + + @Override + public WorldType getWorldType() + { + return worldType; + } + + @Override + public DimensionTypeWrapper getDimensionType() + { + return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType()); + } + + @Override + public int getBlockLight(AbstractBlockPosWrapper blockPos) + { + return world.getLightEngine().blockEngine.getLightValue(((BlockPosWrapper) blockPos).getBlockPos()); + } + + @Override + public int getSkyLight(AbstractBlockPosWrapper blockPos) + { + return world.getLightEngine().skyEngine.getLightValue(((BlockPosWrapper) blockPos).getBlockPos()); + } + + @Override + public BiomeWrapper getBiome(AbstractBlockPosWrapper blockPos) + { + return BiomeWrapper.getBiomeWrapper(world.getBiome(((BlockPosWrapper) blockPos).getBlockPos())); + } + + public IWorld getWorld() + { + return world; + } + + @Override + public boolean hasCeiling() + { + return world.dimensionType().hasCeiling(); + } + + @Override + public boolean hasSkyLight() + { + return world.dimensionType().hasSkyLight(); + } + + @Override + public boolean isEmpty() + { + return world == null; + } + + @Override + public int getHeight() + { + return world.getHeight(); + } + + /** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */ + @Override + public File getSaveFolder() throws UnsupportedOperationException + { + if (worldType != WorldType.ServerWorld) + throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds."); + + ServerChunkProvider chunkSource = ((ServerWorld) world).getChunkSource(); + return chunkSource.dataStorage.dataFolder; + } + + + /** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */ + public ServerWorld getServerWorld() throws UnsupportedOperationException + { + if (worldType != WorldType.ServerWorld) + throw new UnsupportedOperationException("getSaveFolder can only be called for ServerWorlds."); + + return (ServerWorld) world; + } + + @Override + public int getSeaLevel() + { + // TODO this is depreciated, what should we use instead? + return world.getSeaLevel(); + } + + +} diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodServerWorld.java b/src/main/java/com/seibel/lod/forge/wrappers/worldGeneration/LodServerWorld.java similarity index 94% rename from src/main/java/com/seibel/lod/builders/worldGeneration/LodServerWorld.java rename to src/main/java/com/seibel/lod/forge/wrappers/worldGeneration/LodServerWorld.java index 1229a853c..b34eaece5 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodServerWorld.java +++ b/src/main/java/com/seibel/lod/forge/wrappers/worldGeneration/LodServerWorld.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.lod.builders.worldGeneration; +package com.seibel.lod.forge.wrappers.worldGeneration; import java.util.HashMap; import java.util.List; @@ -25,7 +25,8 @@ import java.util.Random; import java.util.function.Predicate; import java.util.stream.Stream; -import com.seibel.lod.util.LodUtil; +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.forge.wrappers.WrapperUtil; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -54,7 +55,6 @@ import net.minecraft.world.chunk.AbstractChunkProvider; import net.minecraft.world.chunk.ChunkStatus; import net.minecraft.world.chunk.IChunk; import net.minecraft.world.gen.Heightmap; -import net.minecraft.world.gen.Heightmap.Type; import net.minecraft.world.gen.feature.structure.Structure; import net.minecraft.world.gen.feature.structure.StructureStart; import net.minecraft.world.lighting.WorldLightManager; @@ -64,9 +64,10 @@ import net.minecraft.world.storage.IWorldInfo; /** * This is a fake ServerWorld used when generating features. - * This allows us to keep each LodChunk generation independent - * of the actual ServerWorld, allowing us - * to multithread generation. + * It allows us to keep each LodChunk generation independent + * of the actual ServerWorld, allowing + * multithread generation. + * * @author James Seibel * @version 7-26-2021 */ @@ -87,7 +88,7 @@ public class LodServerWorld implements ISeedReader @Override - public int getHeight(Type heightmapType, int x, int z) + public int getHeight(Heightmap.Type heightmapType, int x, int z) { // make sure the block position is set relative to the chunk x = x % LodUtil.CHUNK_WIDTH; @@ -96,7 +97,7 @@ public class LodServerWorld implements ISeedReader z = z % LodUtil.CHUNK_WIDTH; z = (z < 0) ? z + 16 : z; - return chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z); + return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(x, z); } @Override diff --git a/src/main/java/com/seibel/lod/forge/wrappers/worldGeneration/WorldGeneratorWrapper.java b/src/main/java/com/seibel/lod/forge/wrappers/worldGeneration/WorldGeneratorWrapper.java new file mode 100644 index 000000000..2eb0c95da --- /dev/null +++ b/src/main/java/com/seibel/lod/forge/wrappers/worldGeneration/WorldGeneratorWrapper.java @@ -0,0 +1,389 @@ +package com.seibel.lod.forge.wrappers.worldGeneration; + +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +import com.seibel.lod.core.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig; +import com.seibel.lod.core.enums.config.DistanceGenerationMode; +import com.seibel.lod.core.objects.lod.LodDimension; +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.util.SingletonHandler; +import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper; +import com.seibel.lod.forge.wrappers.WrapperUtil; +import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper; +import com.seibel.lod.forge.wrappers.chunk.ChunkWrapper; +import com.seibel.lod.forge.wrappers.world.WorldWrapper; + +import net.minecraft.util.palette.UpgradeData; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.ChunkPrimer; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.ChunkGenerator; +import net.minecraft.world.gen.Heightmap; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.IceAndSnowFeature; +import net.minecraft.world.gen.feature.NoFeatureConfig; +import net.minecraft.world.gen.feature.template.TemplateManager; +import net.minecraft.world.server.ServerChunkProvider; +import net.minecraft.world.server.ServerWorld; +import net.minecraft.world.server.ServerWorldLightManager; + +/** + * @author James Seibel + * @version 11-13-2021 + */ +public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper +{ + private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); + + /** + * If a configured feature fails for whatever reason, + * add it to this list. This will hopefully remove any + * features that could cause issues down the line. + */ + private static final ConcurrentHashMap> FEATURES_TO_AVOID = new ConcurrentHashMap<>(); + + + public final ServerWorld serverWorld; + public final LodDimension lodDim; + public final LodBuilder lodBuilder; + + public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) + { + super(newLodBuilder, newLodDimension, worldWrapper); + + lodBuilder = newLodBuilder; + lodDim = newLodDimension; + serverWorld = ((WorldWrapper) worldWrapper).getServerWorld(); + } + + + + + + + + /** takes about 2-5 ms */ + @Override + public void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode) + { + List chunkList = new LinkedList<>(); + ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY); + chunkList.add(chunk); + + ServerChunkProvider chunkSource = serverWorld.getChunkSource(); + ChunkGenerator chunkGen = chunkSource.generator; + + // generate the terrain (this is thread safe) + ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList); + // override the chunk status, so we can run the next generator stage + chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); + chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); + chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); + + + + + // generate fake height data for this LOD + int seaLevel = serverWorld.getSeaLevel(); + + boolean simulateHeight = generationMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; + boolean inTheEnd = false; + + // add fake heightmap data so our LODs aren't at height 0 + Heightmap heightmap = new Heightmap(chunk, WrapperUtil.DEFAULT_HEIGHTMAP); + for (int x = 0; x < LodUtil.CHUNK_WIDTH && !inTheEnd; x++) + { + for (int z = 0; z < LodUtil.CHUNK_WIDTH && !inTheEnd; z++) + { + if (simulateHeight) + { + // these heights are of course aren't super accurate, + // they are just to simulate height data where there isn't any + switch (chunk.getBiomes().getNoiseBiome(x >> 2, seaLevel >> 2, z >> 2).getBiomeCategory()) + { + case NETHER: + heightmap.setHeight(x, z, serverWorld.getHeight() / 2); + break; + + case EXTREME_HILLS: + heightmap.setHeight(x, z, seaLevel + 30); + break; + case MESA: + case JUNGLE: + heightmap.setHeight(x, z, seaLevel + 20); + break; + case BEACH: + heightmap.setHeight(x, z, seaLevel + 5); + break; + case NONE: + heightmap.setHeight(x, z, 0); + break; + + case OCEAN: + case RIVER: + heightmap.setHeight(x, z, seaLevel); + break; + + case THEEND: + inTheEnd = true; + break; + + // DESERT + // FOREST + // ICY + // MUSHROOM + // SAVANNA + // SWAMP + // TAIGA + // PLAINS + default: + heightmap.setHeight(x, z, seaLevel + 10); + break; + }// heightmap switch + } + else + { + // we aren't simulating height + // always use sea level + heightmap.setHeight(x, z, seaLevel); + } + }// z + }// x + + chunk.setHeightmap(WrapperUtil.DEFAULT_HEIGHTMAP, heightmap.getRawData()); + + + if (!inTheEnd) + { + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false)); + } + else + { + // if we are in the end, don't generate any chunks. + // Since we don't know where the islands are, everything + // generates the same, and it looks awful. + //TODO it appears that 'if' can be collapsed, but comment says that it should not be a case + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(true, true, false)); + } + + +// long startTime = System.currentTimeMillis(); +// long endTime = System.currentTimeMillis(); +// System.out.println(endTime - startTime); + } + + + /** takes about 10 - 20 ms */ + @Override + public void generateSurface(AbstractChunkPosWrapper pos) + { + List chunkList = new LinkedList<>(); + ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY); + chunkList.add(chunk); + LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); + + ServerChunkProvider chunkSource = serverWorld.getChunkSource(); + ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine(); + TemplateManager templateManager = serverWorld.getStructureManager(); + ChunkGenerator chunkGen = chunkSource.generator; + + + // generate the terrain (this is thread safe) + ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + // override the chunk status, so we can run the next generator stage + chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); + chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); + ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + + // this feature has been proven to be thread safe, + // so we will add it + IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC); + snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null); + + + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.SURFACE)); + + /*TODO if we want to use Biome utils and terrain utils for overworld + * lodBuilder.generateLodNodeFromChunk(lodDim, pos ,detailLevel, serverWorld.getSeed());*/ + } + + + /** + * takes about 15 - 20 ms + *

+ * Causes concurrentModification Exceptions, + * which could cause instability or world generation bugs + */ + @Override + public void generateFeatures(AbstractChunkPosWrapper pos) + { + List chunkList = new LinkedList<>(); + ChunkPrimer chunk = new ChunkPrimer(((ChunkPosWrapper) pos).getChunkPos(), UpgradeData.EMPTY); + chunkList.add(chunk); + LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); + + ServerChunkProvider chunkSource = serverWorld.getChunkSource(); + ServerWorldLightManager lightEngine = (ServerWorldLightManager) serverWorld.getLightEngine(); + TemplateManager templateManager = serverWorld.getStructureManager(); + ChunkGenerator chunkGen = chunkSource.generator; + + + // generate the terrain (this is thread safe) + ChunkStatus.EMPTY.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + // override the chunk status, so we can run the next generator stage + chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES); + chunkGen.createBiomes(serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunk); + ChunkStatus.NOISE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + ChunkStatus.SURFACE.generate(serverWorld, chunkGen, templateManager, lightEngine, null, chunkList); + + + // get all the biomes in the chunk + HashSet biomes = new HashSet<>(); + for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) + { + for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) + { + Biome biome = chunk.getBiomes().getNoiseBiome(x >> 2, serverWorld.getSeaLevel() >> 2, z >> 2); + + // Issue #35 + // For some reason Jungle biomes cause incredible lag + // the features here must be interacting with each other + // in unpredictable ways (specifically tree feature generation). + // When generating Features my CPU usage generally hovers around 30 - 40% + // when generating Jungles it spikes to 100%. + if (biome.getBiomeCategory() != Biome.Category.JUNGLE) + { + // should probably use the heightmap here instead of seaLevel, + // but this seems to get the job done well enough + biomes.add(biome); + } + } + } + + boolean allowUnstableFeatures = CONFIG.client().worldGenerator().getAllowUnstableFeatureGeneration(); + + // generate all the features related to this chunk. + // this may or may not be thread safe + for (Biome biome : biomes) + { + List>>> featuresForState = biome.generationSettings.features(); + + for (List>> suppliers : featuresForState) + { + for (Supplier> featureSupplier : suppliers) + { + ConfiguredFeature configuredFeature = featureSupplier.get(); + + if (!allowUnstableFeatures && + FEATURES_TO_AVOID.containsKey(configuredFeature.hashCode())) + continue; + + + try + { + configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition()); + } + catch (ConcurrentModificationException | UnsupportedOperationException e) + { + // This will happen. I'm not sure what to do about it + // except pray that it doesn't affect the normal world generation + // in any harmful way. + // Update: this can cause crashes and high CPU usage. + + // Issue #35 + // I tried cloning the config for each feature, but that + // path was blocked since I can't clone lambda methods. + // I tried using a deep cloning library and discovered + // the problem there. + // ( https://github.com/kostaskougios/cloning + // and + // https://github.com/EsotericSoftware/kryo ) + + if (!allowUnstableFeatures) + FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature); +// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); + } + // This will happen when the LodServerWorld + // isn't able to return something that a feature + // generator needs + catch (Exception e) + { + // I'm not sure what happened, print to the log + + e.printStackTrace(); + + if (!allowUnstableFeatures) + FEATURES_TO_AVOID.put(configuredFeature.hashCode(), configuredFeature); +// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount()); + } + } + } + } + + // generate a Lod like normal + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(chunk), new LodBuilderConfig(DistanceGenerationMode.FEATURES)); + } + + + /** + * Generates using MC's ServerWorld. + *

+ * on pre generated chunks 0 - 1 ms
+ * on un generated chunks 0 - 50 ms
+ * with the median seeming to hover around 15 - 30 ms
+ * and outliers in the 100 - 200 ms range
+ *

+ * Note this should not be multithreaded and does cause server/simulation lag + * (Higher lag for generating than loading) + */ + @Override + public void generateFull(AbstractChunkPosWrapper pos) + { + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.FULL)); + } + + + + + + + + /* + * performance/generation tests related to + * serverWorld.getChunk(x, z, ChunkStatus. *** ) + + true/false is whether they generated blocks or not + the time is how long it took to generate + + ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P) + ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks) + ChunkStatus.BIOMES 1 - 10 ms false (no height) + ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone) + ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass) + ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass) + ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass) + ChunkStatus.FEATURES 7 - 25 ms true + ChunkStatus.HEIGHTMAPS 20 - 40 ms true + ChunkStatus.LIGHT 20 - 40 ms true + ChunkStatus.FULL 30 - 50 ms true + ChunkStatus.SPAWN 50 - 80 ms true + + + At this point I would suggest using FEATURES, as it generates snow and trees + (and any other object that are needed to make biomes distinct) + + Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much) + */ +} diff --git a/src/main/java/com/seibel/lod/handlers/ChunkLoader.java b/src/main/java/com/seibel/lod/handlers/ChunkLoader.java deleted file mode 100644 index 392e34a7a..000000000 --- a/src/main/java/com/seibel/lod/handlers/ChunkLoader.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.handlers; - -import java.io.File; - -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.MinecraftWrapper; - -import com.seibel.lod.wrappers.World.LevelWrapper; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.chunk.IChunk; -import net.minecraft.world.chunk.storage.ChunkSerializer; -import net.minecraft.world.server.ServerWorld; - -/** - * - * @author ?? - * @version ?? - */ -public class ChunkLoader -{ - public static IChunk getChunkFromFile(ChunkPos pos){ - - LevelWrapper clientLevel = MinecraftWrapper.INSTANCE.getWrappedClientLevel(); - if (clientLevel == null) - return null; - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientLevel.getWorld().dimensionType()); - try - { - File file = new File(serverWorld.getChunkSource().getDataStorage().dataFolder.getParent() + File.separatorChar + "region", "r." + (pos.x >> 5) + "." + (pos.z >> 5) + ".mca"); - if(!file.exists()) - return null; - IChunk loadedChunk = ChunkSerializer.read( - serverWorld, - serverWorld.getStructureManager(), - serverWorld.getPoiManager(), - pos, - serverWorld.getChunkSource().chunkMap.read(pos) - ); - boolean emptyChunk = true; - for(int i = 0; i < 16; i++){ - for(int j = 0; j < 16; j++){ - emptyChunk &= loadedChunk.isYSpaceEmpty(i,j); - } - } - if(emptyChunk) - return null; - else - return loadedChunk; - } - catch (Exception e) - { - return null; - } - } -} diff --git a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java deleted file mode 100644 index 1c923ff5b..000000000 --- a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.handlers; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; -import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; - -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.enums.VerticalQuality; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.LodRegion; -import com.seibel.lod.objects.RegionPos; -import com.seibel.lod.objects.VerticalLevelContainer; -import com.seibel.lod.proxy.ClientProxy; -import com.seibel.lod.util.LodThreadFactory; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.util.ThreadMapUtil; - -/** - * This object handles creating LodRegions - * from files and saving LodRegion objects - * to file. - * - * @author James Seibel - * @author Cola - * @version 9-25-2021 - */ -public class LodDimensionFileHandler -{ - /** This is the dimension that owns this file handler */ - private LodDimension lodDimension; - - private final File dimensionDataSaveFolder; - - /** lod */ - private static final String FILE_NAME_PREFIX = "lod"; - /** .txt */ - private static final String FILE_EXTENSION = ".xz"; - /** detail- */ - private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-"; - - /** - * .tmp
- * Added to the end of the file path when saving to prevent - * nulling a currently existing file.
- * After the file finishes saving it will end with - * FILE_EXTENSION. - */ - private static final String TMP_FILE_EXTENSION = ".tmp"; - - /** - * This is the file version currently accepted by this - * file handler, older versions (smaller numbers) will be deleted and overwritten, - * newer versions (larger numbers) will be ignored and won't be read. - */ - public static final int LOD_SAVE_FILE_VERSION = 6; - - /** - * Allow saving asynchronously, but never try to save multiple regions - * at a time - */ - private final ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); - - - - - public LodDimensionFileHandler(File newSaveFolder, LodDimension newLodDimension) - { - if (newSaveFolder == null) - throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to."); - - dimensionDataSaveFolder = newSaveFolder; - lodDimension = newLodDimension; - } - - - - //================// - // read from file // - //================// - - /** - * Returns the LodRegion at the given coordinates. - * Returns an empty region if the file doesn't exist. - */ - public LodRegion loadRegionFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality) - { - int regionX = regionPos.x; - int regionZ = regionPos.z; - LodRegion region = new LodRegion(LodUtil.REGION_DETAIL_LEVEL, regionPos, generationMode, verticalQuality); - - for (byte tempDetailLevel = LodUtil.REGION_DETAIL_LEVEL; tempDetailLevel >= detailLevel; tempDetailLevel--) - { - String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel, verticalQuality); - - try - { - // if the fileName was null that means the folder is inaccessible - // for some reason - if (fileName == null) - throw new IllegalArgumentException("Unable to read region [" + regionX + ", " + regionZ + "] file, no fileName."); - - File file = new File(fileName); - if (!file.exists()) - { - //there is no file for current gen mode - //search others above current from the most to the least detailed - DistanceGenerationMode tempGenMode = DistanceGenerationMode.SERVER; - while (tempGenMode != generationMode) - { - fileName = getFileNameAndPathForRegion(regionX, regionZ, tempGenMode, tempDetailLevel, verticalQuality); - if (fileName != null) - { - file = new File(fileName); - if (file.exists()) - break; - } - //decrease gen mode - if (tempGenMode == DistanceGenerationMode.SERVER) - tempGenMode = DistanceGenerationMode.FEATURES; - else if (tempGenMode == DistanceGenerationMode.FEATURES) - tempGenMode = DistanceGenerationMode.SURFACE; - else if (tempGenMode == DistanceGenerationMode.SURFACE) - tempGenMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; - else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT) - tempGenMode = DistanceGenerationMode.BIOME_ONLY; - else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY) - tempGenMode = DistanceGenerationMode.NONE; - } - if (!file.exists()) - //there wasn't a file, don't return anything - continue; - } - - - - // don't try parsing empty files - long dataSize = file.length(); - dataSize -= 1; - if (dataSize > 0) - { - try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(file))) - { - int fileVersion; - fileVersion = inputStream.read(); - - // check if this file can be read by this file handler - if (fileVersion < LOD_SAVE_FILE_VERSION) - { - // the file we are reading is an older version, - // close the reader and delete the file. - inputStream.close(); - file.delete(); - ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")" - + " version found: " + fileVersion - + ", version requested: " + LOD_SAVE_FILE_VERSION - + ". File was been deleted."); - - break; - } - else if (fileVersion > LOD_SAVE_FILE_VERSION) - { - // the file we are reading is a newer version, - // close the reader and ignore the file, we don't - // want to accidentally delete anything the user may want. - inputStream.close(); - ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ")" - + " version found: " + fileVersion - + ", version requested: " + LOD_SAVE_FILE_VERSION - + " this region will not be written to in order to protect the newer file."); - - break; - } - - - // this file is a readable version, - // read the file - byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel); - inputStream.read(data); - inputStream.close(); - - - // add the data to our region - region.addLevelContainer(new VerticalLevelContainer(data)); - } - catch (IOException ioEx) - { - ClientProxy.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: "); - ioEx.printStackTrace(); - } - } - } - catch (Exception e) - { - // the buffered reader encountered a - // problem reading the file - ClientProxy.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + e.getMessage() + "]: "); - e.printStackTrace(); - } - }// for each detail level - - if (region.getMinDetailLevel() >= detailLevel) - region.growTree(detailLevel); - - return region; - } - - - //==============// - // Save to File // - //==============// - - /** Save all dirty regions in this LodDimension to file */ - public void saveDirtyRegionsToFileAsync() - { - fileWritingThreadPool.execute(saveDirtyRegionsThread); - } - - private final Thread saveDirtyRegionsThread = new Thread(() -> - { - try - { - for (int i = 0; i < lodDimension.getWidth(); i++) - { - for (int j = 0; j < lodDimension.getWidth(); j++) - { - if (lodDimension.GetIsRegionDirty(i, j) && lodDimension.getRegionByArrayIndex(i, j) != null) - { - saveRegionToFile(lodDimension.getRegionByArrayIndex(i, j)); - lodDimension.SetIsRegionDirty(i, j, false); - } - } - } - } - catch (Exception e) - { - e.printStackTrace(); - } - }); - - /** - * Save a specific region to disk.
- * Note:
- * 1. If a file already exists for a newer version - * the file won't be written.
- * 2. This will save to the LodDimension that this - * handler is associated with. - */ - private void saveRegionToFile(LodRegion region) - { - for (byte detailLevel = region.getMinDetailLevel(); detailLevel <= LodUtil.REGION_DETAIL_LEVEL; detailLevel++) - { - String fileName = getFileNameAndPathForRegion(region.regionPosX, region.regionPosZ, region.getGenerationMode(), detailLevel, region.getVerticalQuality()); - - // if the fileName was null that means the folder is inaccessible - // for some reason - if (fileName == null) - { - ClientProxy.LOGGER.warn("Unable to save region [" + region.regionPosX + ", " + region.regionPosZ + "] to file, file is inaccessible."); - return; - } - File oldFile = new File(fileName); - //ClientProxy.LOGGER.info("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file."); - byte[] temp = region.getLevel(detailLevel).toDataString(); - - try - { - // make sure the file and folder exists - if (!oldFile.exists()) - { - // the file doesn't exist, - // create it and the folder if need be - if (!oldFile.getParentFile().exists()) - oldFile.getParentFile().mkdirs(); - oldFile.createNewFile(); - } - else - { - // the file exists, make sure it - // is the correct version. - // (to make sure we don't overwrite a newer - // version file if it exists) - int fileVersion = LOD_SAVE_FILE_VERSION; - int isFull = 0; - try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(oldFile))) - { - fileVersion = inputStream.read(); - inputStream.skip(1); - isFull = inputStream.read() & 0b10000000; - inputStream.close(); - } - catch (IOException ex) - { - ex.printStackTrace(); - } - - // check if this file can be written to by the file handler - if (fileVersion > LOD_SAVE_FILE_VERSION) - { - // the file we are reading is a newer version, - // don't write anything, we don't want to accidentally - // delete anything the user may want. - return; - } - if ((temp[1] & 0b10000000) != 0b10000000 && isFull == 0b10000000) - { - // existing file is complete while new one is only partially generate - // this can happen is for some reason loading failed - // this doesn't fix the bug, but at least protects old data - ClientProxy.LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + fileName + "]"); - return; - } - // if we got this far then we are good - // to overwrite the old file - } - // the old file is good, now create a new temporary save file - File newFile = new File(fileName + TMP_FILE_EXTENSION); - try (XZCompressorOutputStream outputStream = new XZCompressorOutputStream(new FileOutputStream(newFile), 3)) - { - // add the version of this file - outputStream.write(LOD_SAVE_FILE_VERSION); - - // add each LodChunk to the file - outputStream.write(temp); - outputStream.close(); - - // overwrite the old file with the new one - Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); - } - catch (IOException ex) - { - ex.printStackTrace(); - } - } - catch (Exception e) - { - ClientProxy.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: "); - e.printStackTrace(); - } - } - } - - - - - - //================// - // helper methods // - //================// - - public byte[] getHashFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality) - { - int regionX = regionPos.x; - int regionZ = regionPos.z; - String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality); - try (InputStream is = Files.newInputStream(Paths.get(fileName))) { - return org.apache.commons.codec.digest.DigestUtils.md5(is); - } - catch (IOException ioEx) - { - ClientProxy.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: "); - ioEx.printStackTrace(); - } - return new byte[0]; - } - - - /** - * Return the name of the file that should contain the - * region at the given x and z.
- * Returns null if this object isn't available to read and write.

- *

- * example: "lod.0.0.txt"
- *

- * Returns null if there is an IO or security Exception. - */ - private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality) - { - try - { - // saveFolder is something like - // ".\Super Flat\DIM-1\data\" - // or - // ".\Super Flat\data\" - return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar + - verticalQuality + File.separatorChar + - generationMode.toString() + File.separatorChar + - DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar + - FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION; - } - catch (IOException | SecurityException e) - { - ClientProxy.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: "); - e.printStackTrace(); - return null; - } - } - -} diff --git a/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java b/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java deleted file mode 100644 index 9ed3373c5..000000000 --- a/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.handlers; - -import com.seibel.lod.enums.FogQuality; -import com.seibel.lod.proxy.ClientProxy; -import com.seibel.lod.wrappers.MinecraftWrapper; -import net.minecraft.util.math.vector.Matrix4f; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -/** - * This object is used to get variables from methods - * where they are private. Specifically the fog setting - * in Optifine. - * @author James Seibel - * @version 9 -25-2021 - */ -public class ReflectionHandler -{ - public static final ReflectionHandler INSTANCE = new ReflectionHandler(); - - public Field ofFogField = null; - public Method vertexBufferUploadMethod = null; - - - - private ReflectionHandler() - { - setupFogField(); - } - - - /** - * finds the Optifine fog type field - */ - private void setupFogField() - { - // get every variable from the entity renderer - Field[] optionFields = MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(); - - // try and find the ofFogType variable in gameSettings - for (Field field : optionFields) - { - if (field.getName().equals("ofFogType")) - { - ofFogField = field; - return; - } - } - - // we didn't find the field, - // either optifine isn't installed, or - // optifine changed the name of the variable - ClientProxy.LOGGER.info(ReflectionHandler.class.getSimpleName() + ": unable to find the Optifine fog field. If Optifine isn't installed this can be ignored."); - } - - - /** - * Get what type of fog optifine is currently set to render. - * @return the fog quality - */ - public FogQuality getFogQuality() - { - if (ofFogField == null) - { - // either optifine isn't installed, - // the variable name was changed, or - // the setup method wasn't called yet. - return FogQuality.FANCY; - } - - int returnNum = 0; - - try - { - returnNum = (int) ofFogField.get(MinecraftWrapper.INSTANCE.getOptions()); - } - catch (IllegalArgumentException | IllegalAccessException e) - { - e.printStackTrace(); - } - - switch (returnNum) - { - default: - case 0: - // optifine's "default" option, - // it should never be called in this case - - // normal options - case 1: - return FogQuality.FAST; - case 2: - return FogQuality.FANCY; - case 3: - return FogQuality.OFF; - } - } - - /** Detect if Vivecraft is present using reflection. Attempts to find the "VRRenderer" class. */ - public boolean detectVivecraft() - { - try - { - Class.forName("org.vivecraft.provider.VRRenderer"); - return true; - } - catch (ClassNotFoundException ignored) - { - System.out.println("Vivecraft not detected."); - } - return false; - } - - /** - * Modifies a projection matrix's clip planes. - * The projection matrix must be in a column-major format. - * @param projectionMatrix The projection matrix to be modified. - * @param n the new near clip plane value. - * @param f the new far clip plane value. - * @return The modified matrix. - */ - public Matrix4f Matrix4fModifyClipPlanes(Matrix4f projectionMatrix, float n, float f) - { - //find the matrix values. - float nMatrixValue = -((f + n) / (f - n)); - float fMatrixValue = -((2 * f * n) / (f - n)); - try - { - //get the fields of the projectionMatrix - Field[] fields = projectionMatrix.getClass().getDeclaredFields(); - //bypass the security protections on the fields that encode near and far plane values. - fields[10].setAccessible(true); - fields[11].setAccessible(true); - //Change the values of the near and far plane. - fields[10].set(projectionMatrix, nMatrixValue); - fields[11].set(projectionMatrix, fMatrixValue); - } - catch (Exception e) - { - e.printStackTrace(); - } - return projectionMatrix; - } -} diff --git a/src/main/java/com/seibel/lod/objects/LevelContainer.java b/src/main/java/com/seibel/lod/objects/LevelContainer.java deleted file mode 100644 index 74692f950..000000000 --- a/src/main/java/com/seibel/lod/objects/LevelContainer.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -/** - * A level container is a quad tree level - */ -public interface LevelContainer -{ - /** - * With this you can add data to the level container - * @param data actual data to add in an array of long format. - * @param posX x position in the detail level - * @param posZ z position in the detail level - * @param index z position in the detail level - * @return true if correctly added, false otherwise - */ - boolean addData(long data, int posX, int posZ, int index); - - /** - * With this you can add data to the level container - * @param data actual data to add in an array of long[] format. - * @param posX x position in the detail level - * @param posZ z position in the detail level - * @return true if correctly added, false otherwise - */ - boolean addVerticalData(long[] data, int posX, int posZ); - - /** - * With this you can add data to the level container - * @param data actual data to add in an array of long format. - * @param posX x position in the detail level - * @param posZ z position in the detail level - * @return true if correctly added, false otherwise - */ - boolean addSingleData(long data, int posX, int posZ); - - /** - * With this you can get data from the level container - * @param posX x position in the detail level - * @param posZ z position in the detail level - * @return the data in long array format - */ - long getData(int posX, int posZ, int index); - - /** - * With this you can get data from the level container - * @param posX x position in the detail level - * @param posZ z position in the detail level - * @return the data in long array format - */ - long getSingleData(int posX, int posZ); - - /** - * @param posX x position in the detail level - * @param posZ z position in the detail level - * @return true only if the data exist - */ - boolean doesItExist(int posX, int posZ); - - /** - * @return return the detailLevel of this level container - */ - byte getDetailLevel(); - - - int getMaxVerticalData(); - - /** Clears the dataPoint at the given array index */ - void clear(int posX, int posZ); - - /** - * This return a level container with detail level lower than the current level. - * The new level container may use information of this level. - * @return the new level container - */ - LevelContainer expand(); - - /** - * @param lowerLevelContainer lower level where we extract the data - * @param posX x position in the detail level to update - * @param posZ z position in the detail level to update - */ - void updateData(LevelContainer lowerLevelContainer, int posX, int posZ); - - /** - * This will give the data to save in the file - * @return data as a String - */ - byte[] toDataString(); - - - /** - * This will give the data to save in the file - * @return data as a String - */ - int getMaxNumberOfLods(); -} diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java deleted file mode 100644 index e6708ba4f..000000000 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ /dev/null @@ -1,905 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -import java.io.File; -import java.io.IOException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.enums.GenerationPriority; -import com.seibel.lod.enums.VerticalQuality; -import com.seibel.lod.handlers.LodDimensionFileHandler; -import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.DetailDistanceUtil; -import com.seibel.lod.util.LevelPosUtil; -import com.seibel.lod.util.LodThreadFactory; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.MinecraftWrapper; - -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.DimensionType; -import net.minecraft.world.server.ServerChunkProvider; -import net.minecraft.world.server.ServerWorld; - - -/** - * This object holds all loaded LOD regions - * for a given dimension.

- * - * Coordinate Standard:
- * Coordinate called posX or posZ are relative LevelPos coordinates
- * unless stated otherwise.
- * - * @author Leonardo Amato - * @author James Seibel - * @version 10-10-2021 - */ -public class LodDimension -{ - public final DimensionType dimension; - - /** measured in regions */ - private volatile int width; - /** measured in regions */ - private volatile int halfWidth; - - // these three variables are private to force use of the getWidth() method - // which is a safer way to get the width then directly asking the arrays - /** stores all the regions in this dimension */ - public volatile LodRegion[][] regions; - - /** stores if the region at the given x and z index needs to be saved to disk */ - private volatile boolean[][] isRegionDirty; - /** stores if the region at the given x and z index needs to be regenerated */ - private volatile boolean[][] regenRegionBuffer; - /** stores if the buffer size at the given x and z index needs to be changed */ - private volatile boolean[][] recreateRegionBuffer; - - /** - * if true that means there are regions in this dimension - * that need to have their buffers rebuilt. - */ - public volatile boolean regenDimensionBuffers = false; - - private LodDimensionFileHandler fileHandler; - - private final RegionPos center; - - /** prevents the cutAndExpandThread from expanding at the same location multiple times */ - private volatile ChunkPos lastExpandedChunk; - /** prevents the cutAndExpandThread from cutting at the same location multiple times */ - private volatile ChunkPos lastCutChunk; - private final ExecutorService cutAndExpandThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - Cut and Expand")); - - /** - * Creates the dimension centered at (0,0) - * @param newWidth in regions - */ - public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newWidth) - { - lastCutChunk = null; - lastExpandedChunk = null; - dimension = newDimension; - width = newWidth; - halfWidth = width / 2; - MinecraftWrapper mc = MinecraftWrapper.INSTANCE; - - if (newDimension != null && lodWorld != null) - { - try - { - File saveDir; - if (mc.hasSinglePlayerServer()) - { - // local world - - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension); - - // provider needs a separate variable to prevent - // the compiler from complaining - ServerChunkProvider provider = serverWorld.getChunkSource(); - saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod"); - } - else - { - // connected to server - - saveDir = new File(mc.getGameDirectory().getCanonicalFile().getPath() + - File.separatorChar + "lod server data" + File.separatorChar + mc.getCurrentDimensionId()); - } - - fileHandler = new LodDimensionFileHandler(saveDir, this); - } - catch (IOException e) - { - // the file handler wasn't able to be created - // we won't be able to read or write any files - } - } - - - regions = new LodRegion[width][width]; - isRegionDirty = new boolean[width][width]; - regenRegionBuffer = new boolean[width][width]; - recreateRegionBuffer = new boolean[width][width]; - - center = new RegionPos(0, 0); - } - - - /** - * Move the center of this LodDimension and move all owned - * regions over by the given x and z offset.

- *

- * Synchronized to prevent multiple moves happening on top of each other. - */ - public synchronized void move(RegionPos regionOffset) - { - int xOffset = regionOffset.x; - int zOffset = regionOffset.z; - - // if the x or z offset is equal to or greater than - // the total width, just delete the current data - // and update the centerX and/or centerZ - if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width) - { - for (int x = 0; x < width; x++) - for (int z = 0; z < width; z++) - regions[x][z] = null; - - // update the new center - center.x += xOffset; - center.z += zOffset; - - return; - } - - - // X - if (xOffset > 0) - { - // move everything over to the left (as the center moves to the right) - for (int x = 0; x < width; x++) - { - for (int z = 0; z < width; z++) - { - if (x + xOffset < width) - regions[x][z] = regions[x + xOffset][z]; - else - regions[x][z] = null; - } - } - } - else - { - // move everything over to the right (as the center moves to the left) - for (int x = width - 1; x >= 0; x--) - { - for (int z = 0; z < width; z++) - { - if (x + xOffset >= 0) - regions[x][z] = regions[x + xOffset][z]; - else - regions[x][z] = null; - } - } - } - - - // Z - if (zOffset > 0) - { - // move everything up (as the center moves down) - for (int x = 0; x < width; x++) - { - for (int z = 0; z < width; z++) - { - if (z + zOffset < width) - regions[x][z] = regions[x][z + zOffset]; - else - regions[x][z] = null; - } - } - } - else - { - // move everything down (as the center moves up) - for (int x = 0; x < width; x++) - { - for (int z = width - 1; z >= 0; z--) - { - if (z + zOffset >= 0) - regions[x][z] = regions[x][z + zOffset]; - else - regions[x][z] = null; - } - } - } - - - // update the new center - center.x += xOffset; - center.z += zOffset; - } - - - /** - * Gets the region at the given LevelPos - *
- * Returns null if the region doesn't exist - * or is outside the loaded area. - */ - public LodRegion getRegion(byte detailLevel, int levelPosX, int levelPosZ) - { - int xRegion = LevelPosUtil.getRegion(detailLevel, levelPosX); - int zRegion = LevelPosUtil.getRegion(detailLevel, levelPosZ); - int xIndex = (xRegion - center.x) + halfWidth; - int zIndex = (zRegion - center.z) + halfWidth; - - if (!regionIsInRange(xRegion, zRegion)) - return null; - // throw new ArrayIndexOutOfBoundsException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " out of range"); - else if (regions[xIndex][zIndex] == null) - return null; - else if (regions[xIndex][zIndex].getMinDetailLevel() > detailLevel) - return null; - //throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel()); - - return regions[xIndex][zIndex]; - } - - /** - * Gets the region at the given X and Z - *
- * Returns null if the region doesn't exist - * or is outside the loaded area. - */ - public LodRegion getRegion(int regionPosX, int regionPosZ) - { - int xIndex = (regionPosX - center.x) + halfWidth; - int zIndex = (regionPosZ - center.z) + halfWidth; - - if (!regionIsInRange(regionPosX, regionPosZ)) - return null; - //throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range"); - - return regions[xIndex][zIndex]; - } - - /** Useful when iterating over every region. */ - public LodRegion getRegionByArrayIndex(int xIndex, int zIndex) - { - return regions[xIndex][zIndex]; - } - - /** - * Overwrite the LodRegion at the location of newRegion with newRegion. - * @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension. - */ - public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException - { - int xIndex = (newRegion.regionPosX - center.x) + halfWidth; - int zIndex = (newRegion.regionPosZ - center.z) + halfWidth; - - if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ)) - // out of range - throw new ArrayIndexOutOfBoundsException("Region " + newRegion.regionPosX + ", " + newRegion.regionPosZ + " out of range"); - - regions[xIndex][zIndex] = newRegion; - } - - - /** - * Deletes nodes that are a higher detail then necessary, freeing - * up memory. - */ - public void cutRegionNodesAsync(int playerPosX, int playerPosZ) - { - ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ)); - - if (lastCutChunk == null) - lastCutChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); - - // don't run the tree cutter multiple times - // for the same location - if (newPlayerChunk.x != lastCutChunk.x || newPlayerChunk.z != lastCutChunk.z) - { - lastCutChunk = newPlayerChunk; - - Thread thread = new Thread(() -> - { - int regionX; - int regionZ; - int minDistance; - byte detail; - byte minAllowedDetailLevel; - - // go over every region in the dimension - for (int x = 0; x < regions.length; x++) - { - for (int z = 0; z < regions.length; z++) - { - regionX = (x + center.x) - halfWidth; - regionZ = (z + center.z) - halfWidth; - - if (regions[x][z] != null) - { - // check what detail level this region should be - // and cut it if it is higher then that - minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ); - detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance); - minAllowedDetailLevel = DetailDistanceUtil.getCutLodDetail(detail); - - if (regions[x][z].getMinDetailLevel() > minAllowedDetailLevel) - { - regions[x][z].cutTree(minAllowedDetailLevel); - recreateRegionBuffer[x][z] = true; - } - } - }// region z - }// region z - }); - - cutAndExpandThread.execute(thread); - } - } - - /** Either expands or loads all regions in the rendered LOD area */ - public void expandOrLoadRegionsAsync(int playerPosX, int playerPosZ) - { - DistanceGenerationMode generationMode = LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get(); - ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ)); - VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get(); - - - if (lastExpandedChunk == null) - lastExpandedChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); - - // don't run the expander multiple times - // for the same location - if (newPlayerChunk.x != lastExpandedChunk.x || newPlayerChunk.z != lastExpandedChunk.z) - { - lastExpandedChunk = newPlayerChunk; - - Thread thread = new Thread(() -> - { - int regionX; - int regionZ; - LodRegion region; - int minDistance; - byte detail; - byte levelToGen; - - for (int x = 0; x < regions.length; x++) - { - for (int z = 0; z < regions.length; z++) - { - regionX = (x + center.x) - halfWidth; - regionZ = (z + center.z) - halfWidth; - final RegionPos regionPos = new RegionPos(regionX, regionZ); - region = regions[x][z]; - - minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ); - detail = DetailDistanceUtil.getTreeGenDetailFromDistance(minDistance); - levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel; - - // check that the region isn't null and at least this detail level - if (region == null || region.getGenerationMode() != generationMode) - { - // First case, region has to be created - - // try to get the region from file - regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality); - - // if there is no region file create an empty region - if (regions[x][z] == null) - regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, verticalQuality); - - regenRegionBuffer[x][z] = true; - regenDimensionBuffers = true; - recreateRegionBuffer[x][z] = true; - } - else if (region.getMinDetailLevel() > levelToGen) - { - // Second case, the region exists at a higher detail level. - - // Expand the region by introducing the missing layer - region.growTree(levelToGen); - regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality); - recreateRegionBuffer[x][z] = true; - } - } - } - }); - - cutAndExpandThread.execute(thread); - } - } - - /** - * Use addVerticalData when possible. - * Add the given LOD to this dimension at the coordinate - * stored in the LOD. If an LOD already exists at the given - * coordinate it will be overwritten. - */ - public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data, boolean dontSave) - { - int regionPosX = LevelPosUtil.getRegion(detailLevel, posX); - int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ); - - // don't continue if the region can't be saved - LodRegion region = getRegion(regionPosX, regionPosZ); - if (region == null) - return false; - - boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, data); - - // only save valid LODs to disk - if (!dontSave && fileHandler != null) - { - try - { - // mark the region as dirty, so it will be saved to disk - int xIndex = (regionPosX - center.x) + halfWidth; - int zIndex = (regionPosZ - center.z) + halfWidth; - - isRegionDirty[xIndex][zIndex] = true; - regenRegionBuffer[xIndex][zIndex] = true; - regenDimensionBuffers = true; - } - catch (ArrayIndexOutOfBoundsException e) - { - e.printStackTrace(); - // If this happens, the method was probably - // called when the dimension was changing size. - // Hopefully this shouldn't be an issue. - } - } - - return nodeAdded; - } - - /** - * Add whole column of LODs to this dimension at the coordinate - * stored in the LOD. If an LOD already exists at the given - * coordinate it will be overwritten. - */ - public Boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data, boolean dontSave) - { - int regionPosX = LevelPosUtil.getRegion(detailLevel, posX); - int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ); - - // don't continue if the region can't be saved - LodRegion region = getRegion(regionPosX, regionPosZ); - if (region == null) - return false; - - boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, data); - - // only save valid LODs to disk - if (!dontSave && fileHandler != null) - { - try - { - // mark the region as dirty, so it will be saved to disk - int xIndex = (regionPosX - center.x) + halfWidth; - int zIndex = (regionPosZ - center.z) + halfWidth; - - isRegionDirty[xIndex][zIndex] = true; - regenRegionBuffer[xIndex][zIndex] = true; - regenDimensionBuffers = true; - } - catch (ArrayIndexOutOfBoundsException e) - { - e.printStackTrace(); - // If this happens, the method was probably - // called when the dimension was changing size. - // Hopefully this shouldn't be an issue. - } - } - - return nodeAdded; - } - - /** marks the region at the given region position to have its buffer rebuilt */ - public void markRegionBufferToRegen(int xRegion, int zRegion) - { - int xIndex = (xRegion - center.x) + halfWidth; - int zIndex = (zRegion - center.z) + halfWidth; - regenRegionBuffer[xIndex][zIndex] = true; - } - - /** - * Returns every position that need to be generated based on the position of the player - */ - public PosToGenerateContainer getPosToGenerate(int maxDataToGenerate, int playerBlockPosX, int playerBlockPosZ) - { - PosToGenerateContainer posToGenerate; - LodRegion lodRegion; - // all the following values are used for the spiral matrix visit - // x and z are the matrix coord - // dx and dz is the next move on the coordinate in the range -1 0 +1 - int x, z, dx, dz, t; - x = 0; - z = 0; - dx = 0; - dz = -1; - - // We can use two type of generation scheduling - switch (LodConfig.CLIENT.worldGenerator.generationPriority.get()) - { - default: - case NEAR_FIRST: - //in the NEAR_FIRST generation scheduling we prioritize the nearest un-generated position to the player - //the chunk position to generate will be stored in a posToGenerate object - posToGenerate = new PosToGenerateContainer((byte) 10, maxDataToGenerate, playerBlockPosX, playerBlockPosZ); - - int playerChunkX = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerBlockPosX); - int playerChunkZ = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerBlockPosZ); - - int complexity; - int xChunkToCheck; - int zChunkToCheck; - byte detailLevel; - int posX; - int posZ; - long data; - int numbChunksWide = (width) * 32; - int circleLimit = Integer.MAX_VALUE; - - //posToGenerate is using an insertion sort algorithm which can become really fast if the - //original data order is almost ordered. For this reason we explore the matrix of the position to generate - //with a spiral matrix visit (a square spiral is almost ordered in the "nearest to farthest" order) - for (int i = 0; i < numbChunksWide * numbChunksWide; i++) - { - //Firstly we check if the posToGenerate has been filled - if (maxDataToGenerate == 0) - { - maxDataToGenerate--; - //if it has been filled then we set a stop distance - //the stop distance will be current distance (generically x) per square root of 2 - //this would guarantee a circular generation since (Math.abs(x) * 1.41f) is the - //radius of a circle that inscribe a square - circleLimit = (int) (Math.abs(x) * 1.41f); - } - //This second if check if we reached the circleLimit decided in the previous if - //if so we stop - else if (maxDataToGenerate < 0) - { - if (circleLimit < Math.abs(x) && circleLimit < Math.abs(z)) - break; - } - - - xChunkToCheck = x + playerChunkX; - zChunkToCheck = z + playerChunkZ; - - //we get the lod region in which the chunk is present - lodRegion = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, zChunkToCheck); - if (lodRegion == null) - continue; - - //Now we check if the current chunk has been generated with the correct complexity - //if(lodRegion.isChunkPreGenerated(xChunkToCheck,zChunkToCheck)) - // complexity = DistanceGenerationMode.SERVER.complexity; - //else - complexity = LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get().complexity; - - - //we create the level position info of the chunk - detailLevel = lodRegion.getMinDetailLevel(); - posX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, detailLevel); - posZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, zChunkToCheck, detailLevel); - - data = getSingleData(detailLevel, posX, posZ); - - //we will generate the position only if the current generation complexity is lower than the target one. - //an un-generated area will always have 0 generation - if (DataPointUtil.getGenerationMode(data) < complexity) - { - posToGenerate.addPosToGenerate(detailLevel, posX, posZ); - if (maxDataToGenerate >= 0) - maxDataToGenerate--; - } - - //with this code section we find the next chunk to check - if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) - { - t = dx; - dx = -dz; - dz = t; - } - x += dx; - z += dz; - } - break; - - - case FAR_FIRST: - //in the FAR_FIRST generation we dedicate part of the generation process to the far region with really - //low detail quality. - - posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, playerBlockPosX, playerBlockPosZ); - - int xRegion; - int zRegion; - - for (int i = 0; i < width * width; i++) - { - xRegion = x + center.x; - zRegion = z + center.z; - - //All of this is handled directly by the region, which scan every pos from top to bottom of the quad tree - lodRegion = getRegion(xRegion, zRegion); - if (lodRegion != null) - lodRegion.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ); - - - //with this code section we find the next chunk to check - if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) - { - t = dx; - dx = -dz; - dz = t; - } - x += dx; - z += dz; - } - break; - } - return posToGenerate; - } - - /** - * Fills the posToRender with the position to render for the regionPos given in input - */ - public void getPosToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX, - int playerPosZ) - { - LodRegion region = getRegion(regionPos.x, regionPos.z); - if (region != null) - region.getPosToRender(posToRender, playerPosX, playerPosZ, LodConfig.CLIENT.worldGenerator.generationPriority.get() == GenerationPriority.NEAR_FIRST); - } - - /** - * Determines how many vertical LODs could be used - * for the given region at the given detail level - */ - public int getMaxVerticalData(byte detailLevel, int posX, int posZ) - { - if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) - throw new IllegalArgumentException("getMaxVerticalData given a level of [" + detailLevel + "] when [" + LodUtil.REGION_DETAIL_LEVEL + "] is the max."); - - LodRegion region = getRegion(detailLevel, posX, posZ); - if (region == null) - return 0; - - return region.getMaxVerticalData(detailLevel); - } - - /** - * Get the data point at the given X and Z coordinates - * in this dimension. - *
- * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - public long getData(byte detailLevel, int posX, int posZ, int verticalIndex) - { - if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) - throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); - - LodRegion region = getRegion(detailLevel, posX, posZ); - if (region == null) - return DataPointUtil.EMPTY_DATA; - - return region.getData(detailLevel, posX, posZ, verticalIndex); - } - - - /** - * Get the data point at the given X and Z coordinates - * in this dimension. - *
- * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - public long getSingleData(byte detailLevel, int posX, int posZ) - { - if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) - throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); - - LodRegion region = getRegion(detailLevel, posX, posZ); - if (region == null) - return DataPointUtil.EMPTY_DATA; - - return region.getSingleData(detailLevel, posX, posZ); - } - - /** Clears the given region */ - public void clear(byte detailLevel, int posX, int posZ) - { - if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) - throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); - - LodRegion region = getRegion(detailLevel, posX, posZ); - if (region == null) - return; - - region.clear(detailLevel, posX, posZ); - } - - /** - * Returns if the buffer at the given array index needs - * to have its buffer regenerated. - */ - public boolean doesRegionNeedBufferRegen(int xIndex, int zIndex) - { - return regenRegionBuffer[xIndex][zIndex] || recreateRegionBuffer[xIndex][zIndex]; - } - - - /** - * Sets if the buffer at the given array index needs - * to have its buffer regenerated. - */ - public void setRegenRegionBufferByArrayIndex(int xIndex, int zIndex, boolean newRegen) - { - regenRegionBuffer[xIndex][zIndex] = newRegen; - } - - /** - * Get the data point at the given LevelPos - * in this dimension. - *
- * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - public void updateData(byte detailLevel, int posX, int posZ) - { - if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) - throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); - - LodRegion region = getRegion(detailLevel, posX, posZ); - if (region == null) - return; - - region.updateArea(detailLevel, posX, posZ); - } - - /** Returns true if a region exists at the given LevelPos */ - public boolean doesDataExist(byte detailLevel, int posX, int posZ) - { - LodRegion region = getRegion(detailLevel, posX, posZ); - return region != null && region.doesDataExist(detailLevel, posX, posZ); - } - - /** - * Loads the region at the given RegionPos from file, - * if a file exists for that region. - */ - public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel, - DistanceGenerationMode generationMode, VerticalQuality verticalQuality) - { - return fileHandler != null ? fileHandler.loadRegionFromFile(detailLevel, regionPos, generationMode, verticalQuality) : null; - } - - /** Save all dirty regions in this LodDimension to file. */ - public void saveDirtyRegionsToFileAsync() - { - fileHandler.saveDirtyRegionsToFileAsync(); - } - - - /** Return true if the chunk has been pregenerated in game */ - //public boolean isChunkPreGenerated(int xChunkPos, int zChunkPos) - //{ - // - // LodRegion region = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPos, zChunkPos); - // if (region == null) - // return false; - // - // return region.isChunkPreGenerated(xChunkPos, zChunkPos); - //} - - /** - * Returns whether the region at the given RegionPos - * is within the loaded range. - */ - public boolean regionIsInRange(int regionX, int regionZ) - { - int xIndex = (regionX - center.x) + halfWidth; - int zIndex = (regionZ - center.z) + halfWidth; - - return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width; - } - - /** Returns the dimension's center region position X value */ - public int getCenterRegionPosX() - { - return center.x; - } - - /** Returns the dimension's center region position Z value */ - public int getCenterRegionPosZ() - { - return center.z; - } - - /** returns the width of the dimension in regions */ - public int getWidth() - { - // we want to get the length directly from the - // source to make sure it is in sync with region - // and isRegionDirty - return regions != null ? regions.length : width; - } - - /** Update the width of this dimension, in regions */ - public void setRegionWidth(int newWidth) - { - width = newWidth; - halfWidth = width/ 2; - - regions = new LodRegion[width][width]; - isRegionDirty = new boolean[width][width]; - regenRegionBuffer = new boolean[width][width]; - recreateRegionBuffer = new boolean[width][width]; - - // populate isRegionDirty - for (int i = 0; i < width; i++) - for (int j = 0; j < width; j++) - isRegionDirty[i][j] = false; - } - - - @Override - public String toString() - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("Dimension : \n"); - for (LodRegion[] lodRegions : regions) - { - for (LodRegion region : lodRegions) - { - if (region == null) - stringBuilder.append("n"); - else - stringBuilder.append(region.getMinDetailLevel()); - stringBuilder.append("\t"); - } - stringBuilder.append("\n"); - } - return stringBuilder.toString(); - } - - public boolean GetIsRegionDirty(int i, int j) - { - return isRegionDirty[i][j]; - } - - public void SetIsRegionDirty(int i, int j, boolean val) - { - isRegionDirty[i][j] = val; - } -} diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java deleted file mode 100644 index f4719897e..000000000 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ /dev/null @@ -1,609 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.enums.VerticalQuality; -import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.DetailDistanceUtil; -import com.seibel.lod.util.LevelPosUtil; -import com.seibel.lod.util.LodUtil; - -/** - * This object holds all loaded LevelContainers acting as a quad tree - * for a given region.

- * - * Coordinate Standard:
- * Coordinate called posX or posZ are relative LevelPos coordinates
- * unless stated otherwise.
- * - * @author Leonardo Amato - * @version 10-10-2021 - */ -public class LodRegion -{ - /** Number of detail level supported by a region */ - private static final byte POSSIBLE_LOD = 10; - - - /** Holds the lowest (least detailed) detail level in this region */ - private byte minDetailLevel; - - /** - * This holds all data for this region - */ - private final LevelContainer[] dataContainer; - - /** This chunk Pos has been generated */ - //private final boolean[] preGeneratedChunkPos; - - /** the generation mode for this region */ - private final DistanceGenerationMode generationMode; - /** the vertical quality of this region */ - private final VerticalQuality verticalQuality; - - /** this region's x RegionPos */ - public final int regionPosX; - /** this region's z RegionPos */ - public final int regionPosZ; - - public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality) - { - this.minDetailLevel = minDetailLevel; - this.regionPosX = regionPos.x; - this.regionPosZ = regionPos.z; - this.verticalQuality = verticalQuality; - this.generationMode = generationMode; - dataContainer = new LevelContainer[POSSIBLE_LOD]; - - - // Initialize all the different matrices - for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) - { - dataContainer[lod] = new VerticalLevelContainer(lod); - } - - boolean fileFound = false; - - /* - preGeneratedChunkPos = new boolean[32 * 32]; - if (MinecraftWrapper.INSTANCE.hasSinglePlayerServer() && LodConfig.CLIENT.worldGenerator.useExperimentalPreGenLoading.get()) - { - File regionFileDirHead; - File regionFileDirParent; - // local world - - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(MinecraftWrapper.INSTANCE.getCurrentDimension()); - - // provider needs a separate variable to prevent - // the compiler from complaining - StringBuilder string = new StringBuilder(); - try - { - ServerChunkProvider provider = serverWorld.getChunkSource(); - - //System.out.println(provider.dataStorage.dataFolder); - regionFileDirHead = new File(provider.dataStorage.dataFolder.getCanonicalFile().getParentFile().toPath().toAbsolutePath().toString() + File.separatorChar + "region", "r." + regionPosZ + "." + regionPosX + ".mca"); - if (regionFileDirHead.exists()) - { - regionFileDirParent = regionFileDirHead.getParentFile(); - //string.append(regionFileDirParent.toString()); - string.append(regionFileDirHead); - RegionFile regionFile = new RegionFile(regionFileDirHead, regionFileDirParent, true); - for (int x = 0; x < 32; x++) - { - for (int z = 0; z < 32; z++) - { - preGeneratedChunkPos[x * 32 + z] = regionFile.doesChunkExist(new ChunkPos(regionPosX * 32 + x, regionPosZ * 32 + z)); - } - } - - string.append("region " + regionPosX + " " + regionPosZ + "\n"); - for (int x = 0; x < 32; x++) - { - for (int z = 0; z < 32; z++) - { - //regionFile.doesChunkExist() - string.append(preGeneratedChunkPos[x * 32 + z] + "\t"); - } - string.append("\n"); - } - regionFile.close(); - } - } - catch (Exception e) - { - e.printStackTrace(); - } - System.out.println(string); - }*/ - - } - - - /** Return true if the chunk has been pregenerated in game */ - //public boolean isChunkPreGenerated(int xChunkPos, int zChunkPos) - //{ - // xChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPos); - // zChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, zChunkPos); - // return preGeneratedChunkPos[xChunkPos * 32 + zChunkPos]; - //} - - /** - * Inserts the data point into the region. - *

- * TODO this will always return true unless it has - * @return true if the data was added successfully - */ - public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data) - { - posX = LevelPosUtil.getRegionModule(detailLevel, posX); - posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - - // The dataContainer could have null entries if the - // detailLevel changes. - if (this.dataContainer[detailLevel] == null) - { - this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel); - } - - this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex); - - return true; - } - - /** - * Inserts the vertical data into the region. - *

- * TODO this will always return true unless it has - * @return true if the data was added successfully - */ - public boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data) - { - //position is already relative - //posX = LevelPosUtil.getRegionModule(detailLevel, posX); - //posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - - // The dataContainer could have null entries if the - // detailLevel changes. - if (this.dataContainer[detailLevel] == null) - this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel); - - return this.dataContainer[detailLevel].addVerticalData(data, posX, posZ); - } - - /** - * Get the dataPoint at the given relative position. - * @return the data at the relative pos and detail level, - * 0 if the data doesn't exist. - */ - public long getData(byte detailLevel, int posX, int posZ, int verticalIndex) - { - return dataContainer[detailLevel].getData(posX, posZ, verticalIndex); - } - - /** - * Get the dataPoint at the given relative position. - * @return the data at the relative pos and detail level, - * 0 if the data doesn't exist. - */ - public long getSingleData(byte detailLevel, int posX, int posZ) - { - return dataContainer[detailLevel].getSingleData(posX, posZ); - } - - /** - * Clears the datapoint at the given relative position - */ - public void clear(byte detailLevel, int posX, int posZ) - { - dataContainer[detailLevel].clear(posX, posZ); - } - - /** - * This method will fill the posToGenerate array with all levelPos that - * are render-able. - *

- * TODO why don't we return the posToGenerate, it would make this easier to understand - */ - public void getPosToGenerate(PosToGenerateContainer posToGenerate, - int playerBlockPosX, int playerBlockPosZ) - { - getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ); - - } - - /** - * A recursive method that fills the posToGenerate array with all levelPos that - * need to be generated. - *

- * TODO why don't we return the posToGenerate, it would make this easier to understand - */ - private void getPosToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel, - int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ) - { - // equivalent to 2^(...) - int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - - // calculate what LevelPos are in range to generate - int maxDistance = LevelPosUtil.maxDistance(detailLevel, childOffsetPosX, childOffsetPosZ, playerPosX, playerPosZ, regionPosX, regionPosZ); - - // determine this child's levelPos - byte childDetailLevel = (byte) (detailLevel - 1); - int childPosX = childOffsetPosX * 2; - int childPosZ = childOffsetPosZ * 2; - - int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - childDetailLevel); - - byte targetDetailLevel = DetailDistanceUtil.getLodGenDetail(DetailDistanceUtil.getGenerationDetailFromDistance(maxDistance)).detailLevel; - if (targetDetailLevel <= detailLevel) - { - if (targetDetailLevel == detailLevel) - { - if (!doesDataExist(detailLevel, childOffsetPosX, childOffsetPosZ)) - posToGenerate.addPosToGenerate(detailLevel, childOffsetPosX + regionPosX * size, childOffsetPosZ + regionPosZ * size); - } - else - { - // we want at max one request per chunk (since the world generator creates chunks). - // So for lod smaller than a chunk, only recurse down - // the top right child - - if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL) - { - int ungeneratedChildren = 0; - - // make sure all children are generated to this detailLevel - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - if (!doesDataExist(childDetailLevel, childPosX + x, childPosZ + z)) - { - ungeneratedChildren++; - posToGenerate.addPosToGenerate(childDetailLevel, childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize); - } - } - } - - // only if all the children are correctly generated - // should we go deeper - if (ungeneratedChildren == 0) - for (int x = 0; x <= 1; x++) - for (int z = 0; z <= 1; z++) - getPosToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ); - } - else - { - // The detail Level is smaller than a chunk. - // Only recurse down the top right child. - - if (DetailDistanceUtil.getLodGenDetail(childDetailLevel).detailLevel <= (childDetailLevel)) - { - if (!doesDataExist(childDetailLevel, childPosX, childPosZ)) - posToGenerate.addPosToGenerate(childDetailLevel, childPosX + regionPosX * childSize, childPosZ + regionPosZ * childSize); - else - getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ); - } - } - } - } - // we have gone beyond the target Detail level - // we can stop generating - - } - - - /** - * This method will fill the posToRender array with all levelPos that - * are render-able. - *

- * TODO why don't we return the posToRender, it would make this easier to understand - */ - public void getPosToRender(PosToRenderContainer posToRender, - int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel) - { - getPosToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ, requireCorrectDetailLevel); - } - - /** - * This method will fill the posToRender array with all levelPos that - * are render-able. - *

- * TODO why don't we return the posToRender, it would make this easier to understand - * TODO this needs some more comments, James was only able to figure out part of it - */ - private void getPosToRender(PosToRenderContainer posToRender, - byte detailLevel, int posX, int posZ, - int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel) - { - // equivalent to 2^(...) - int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - - byte desiredLevel; - int maxDistance; - int minDistance; - int childLevel; - - - // calculate the LevelPos that are in range - maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ); - desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance)); - minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ); - childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance)); - - if (detailLevel == childLevel - 1) - { - posToRender.addPosToRender(detailLevel, - posX + regionPosX * size, - posZ + regionPosZ * size); - } - else - //if (desiredLevel > detailLevel) - //{ - // we have gone beyond the target Detail level - // we can stop generating - //} else - if (desiredLevel == detailLevel) - { - posToRender.addPosToRender(detailLevel, - posX + regionPosX * size, - posZ + regionPosZ * size); - } - else //case where (detailLevel > desiredLevel) - { - int childPosX = posX * 2; - int childPosZ = posZ * 2; - byte childDetailLevel = (byte) (detailLevel - 1); - int childrenCount = 0; - - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z)) - { - if (!requireCorrectDetailLevel) - childrenCount++; - else - getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel); - } - } - } - - - if (!requireCorrectDetailLevel) - { - // If all the four children exist go deeper - if (childrenCount == 4) - { - for (int x = 0; x <= 1; x++) - for (int z = 0; z <= 1; z++) - getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel); - } - else - { - posToRender.addPosToRender(detailLevel, - posX + regionPosX * size, - posZ + regionPosZ * size); - } - } - } - } - - - /** - * Updates all children. - *

- * TODO could this be renamed mergeArea? - */ - public void updateArea(byte detailLevel, int posX, int posZ) - { - int width; - int startX; - int startZ; - - // TODO what are each of these loops updating? - for (byte down = (byte) (minDetailLevel + 1); down <= detailLevel; down++) - { - startX = LevelPosUtil.convert(detailLevel, posX, down); - startZ = LevelPosUtil.convert(detailLevel, posZ, down); - width = 1 << (detailLevel - down); - - for (int x = 0; x < width; x++) - for (int z = 0; z < width; z++) - update(down, startX + x, startZ + z); - } - - - for (byte up = (byte) (detailLevel + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++) - { - update(up, - LevelPosUtil.convert(detailLevel, posX, up), - LevelPosUtil.convert(detailLevel, posZ, up)); - } - } - - /** - * Update the child at the given relative Pos - *

- * TODO could this be renamed mergeChildData? - */ - private void update(byte detailLevel, int posX, int posZ) - { - dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ); - } - - - /** - * Returns if data exists at the given relative Pos. - */ - public boolean doesDataExist(byte detailLevel, int posX, int posZ) - { - if (detailLevel < minDetailLevel) - return false; - - posX = LevelPosUtil.getRegionModule(detailLevel, posX); - posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - - if (dataContainer[detailLevel] == null) - return false; - - return dataContainer[detailLevel].doesItExist(posX, posZ); - } - - /** - * Gets the generation mode for the data point at the given relative pos. - */ - public byte getGenerationMode(byte detailLevel, int posX, int posZ) - { - if (dataContainer[detailLevel].doesItExist(posX, posZ)) - // We take the bottom information always - // TODO what does that mean? bottom of what? - return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleData(posX, posZ)); - else - return DistanceGenerationMode.NONE.complexity; - } - - /** - * Returns the lowest (least detailed) detail level in this region - * TODO is that right? - */ - public byte getMinDetailLevel() - { - return minDetailLevel; - } - - /** - * Returns the LevelContainer for the detailLevel - * @throws IllegalArgumentException if the detailLevel is less than minDetailLevel - */ - public LevelContainer getLevel(byte detailLevel) - { - if (detailLevel < minDetailLevel) - throw new IllegalArgumentException("getLevel asked for a detail level that does not exist: minimum: [" + minDetailLevel + "] level requested: [" + detailLevel + "]"); - - return dataContainer[detailLevel]; - } - - /** - * Add the levelContainer to this Region, updating the minDetailLevel - * if necessary. - * @throws IllegalArgumentException if the LevelContainer's detailLevel - * is 2 or more detail levels lower than the - * minDetailLevel of this region. - */ - public void addLevelContainer(LevelContainer levelContainer) - { - if (levelContainer.getDetailLevel() < minDetailLevel - 1) - { - throw new IllegalArgumentException( - "the LevelContainer's detailLevel was " - + "[" + levelContainer.getDetailLevel() + "] but this region " - + "only allows adding LevelContainers with a " - + "detail level of [" + (minDetailLevel - 1) + "]"); - } - - if (levelContainer.getDetailLevel() == minDetailLevel - 1) - minDetailLevel = levelContainer.getDetailLevel(); - - dataContainer[levelContainer.getDetailLevel()] = levelContainer; - } - - // TODO James thinks cutTree and growTree (which he renamed to match cutTree) - // should have more descriptive names, to make sure the "Tree" portion isn't - // confused with Minecraft trees (the plant). - - /** - * Removes any dataContainers that are higher than - * the given detailLevel - */ - public void cutTree(byte detailLevel) - { - if (detailLevel > minDetailLevel) - { - for (byte detailLevelIndex = 0; detailLevelIndex < detailLevel; detailLevelIndex++) - dataContainer[detailLevelIndex] = null; - - minDetailLevel = detailLevel; - } - } - - /** - * Make this region more detailed to the detailLevel given. - * TODO is that correct? - */ - public void growTree(byte detailLevel) - { - if (detailLevel < minDetailLevel) - { - for (byte detailLevelIndex = (byte) (minDetailLevel - 1); detailLevelIndex >= detailLevel; detailLevelIndex--) - { - if (dataContainer[detailLevelIndex + 1] == null) - dataContainer[detailLevelIndex + 1] = new VerticalLevelContainer((byte) (detailLevelIndex + 1)); - - dataContainer[detailLevelIndex] = dataContainer[detailLevelIndex + 1].expand(); - } - minDetailLevel = detailLevel; - } - } - - /** - * return RegionPos of this lod region - */ - public RegionPos getRegionPos() - { - return new RegionPos(regionPosX, regionPosZ); - } - - /** - * Returns how many LODs are in this region - */ - public int getNumberOfLods() - { - int count = 0; - for (LevelContainer container : dataContainer) - count += container.getMaxNumberOfLods(); - - return count; - } - - public VerticalQuality getVerticalQuality() - { - return verticalQuality; - } - - public DistanceGenerationMode getGenerationMode() - { - return generationMode; - } - - public int getMaxVerticalData(byte detailLevel) - { - return dataContainer[detailLevel].getMaxVerticalData(); - } - - - @Override - public String toString() - { - return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString(); - } -} diff --git a/src/main/java/com/seibel/lod/objects/LodWorld.java b/src/main/java/com/seibel/lod/objects/LodWorld.java deleted file mode 100644 index b549aebb7..000000000 --- a/src/main/java/com/seibel/lod/objects/LodWorld.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -import java.util.Hashtable; -import java.util.Map; - -import com.seibel.lod.proxy.ClientProxy; - -import com.seibel.lod.wrappers.World.DimensionTypeWrapper; -import net.minecraft.world.DimensionType; - -/** - * This stores all LODs for a given world. - * - * @author James Seibel - * @author Leonardo Amato - * @version 9-27-2021 - */ -public class LodWorld -{ - /** name of this world */ - private String worldName; - - /** dimensions in this world */ - private Map lodDimensions; - - /** If true then the LOD world is setup and ready to use */ - private boolean isWorldLoaded = false; - - /** the name given to the world if it isn't loaded */ - public static final String NO_WORLD_LOADED = "No world loaded"; - - - - public LodWorld() - { - worldName = NO_WORLD_LOADED; - } - - - - /** - * Set up the LodWorld with the given newWorldName.
- * This should be done whenever loading a new world.

- *

- * Note a System.gc() call may be in order after calling this
- * since a lot of LOD data is now homeless.
- * @param newWorldName name of the world - */ - public void selectWorld(String newWorldName) - { - if (newWorldName.isEmpty()) - { - deselectWorld(); - return; - } - - if (worldName.equals(newWorldName)) - // don't recreate everything if we - // didn't actually change worlds - return; - - worldName = newWorldName; - lodDimensions = new Hashtable<>(); - isWorldLoaded = true; - } - - /** - * Set the worldName to "No world loaded" - * and clear the lodDimensions Map.
- * This should be done whenever unloaded a world.

- *

- * Note a System.gc() call may be in order after calling this
- * since a lot of LOD data is now homeless.
- */ - public void deselectWorld() - { - worldName = NO_WORLD_LOADED; - lodDimensions = null; - isWorldLoaded = false; - } - - - /** - * Adds newDimension to this world, if a LodDimension - * already exists for the given dimension it is replaced. - */ - public void addLodDimension(LodDimension newDimension) - { - if (lodDimensions == null) - return; - - lodDimensions.put(DimensionTypeWrapper.getDimensionTypeWrapper(newDimension.dimension), newDimension); - } - - /** - * Returns null if no LodDimension exists for the given dimension - */ - public LodDimension getLodDimension(DimensionType dimension) - { - if (lodDimensions == null) - return null; - - return lodDimensions.get(DimensionTypeWrapper.getDimensionTypeWrapper(dimension)); - } - - /** - * Resizes the max width in regions that each LodDimension - * should use. - */ - public void resizeDimensionRegionWidth(int newRegionWidth) - { - if (lodDimensions == null) - return; - - saveAllDimensions(); - - for (DimensionTypeWrapper key : lodDimensions.keySet()) - lodDimensions.get(key).setRegionWidth(newRegionWidth); - } - - /** - * Requests all dimensions save any dirty regions they may have. - */ - public void saveAllDimensions() - { - if (lodDimensions == null) - return; - - // TODO we should only print this if lods were actually saved to file - // but that requires a LodDimension.hasDirtyRegions() method or something similar - ClientProxy.LOGGER.info("Saving LODs"); - - for (DimensionTypeWrapper key : lodDimensions.keySet()) - lodDimensions.get(key).saveDirtyRegionsToFileAsync(); - } - - - public boolean getIsWorldNotLoaded() - { - return !isWorldLoaded; - } - - public String getWorldName() - { - return worldName; - } - - @Override - public String toString() - { - return "World name: " + worldName; - } -} - diff --git a/src/main/java/com/seibel/lod/objects/NearFarFogSettings.java b/src/main/java/com/seibel/lod/objects/NearFarFogSettings.java deleted file mode 100644 index 3c25c59c6..000000000 --- a/src/main/java/com/seibel/lod/objects/NearFarFogSettings.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -import com.seibel.lod.enums.FogDistance; -import com.seibel.lod.enums.FogQuality; - -/** - * This object is just a replacement for an array - * to make things easier to understand in the LodRenderer. - * - * @author James Seibel - * @version 7-03-2021 - */ -public class NearFarFogSettings -{ - public final NearOrFarSetting near = new NearOrFarSetting(FogDistance.NEAR); - public final NearOrFarSetting far = new NearOrFarSetting(FogDistance.FAR); - - /** - * If true that means Minecraft is - * rendering fog - */ - public boolean vanillaIsRenderingFog = true; - - public NearFarFogSettings() - { - - } - - - - /** - * This holds all relevant data to rendering fog at either - * near or far distances. - */ - public static class NearOrFarSetting - { - public FogQuality quality = FogQuality.FANCY; - public FogDistance distance; - - public NearOrFarSetting(FogDistance newFogDistance) - { - distance = newFogDistance; - } - } -} diff --git a/src/main/java/com/seibel/lod/objects/PosToGenerateContainer.java b/src/main/java/com/seibel/lod/objects/PosToGenerateContainer.java deleted file mode 100644 index 5efa58d14..000000000 --- a/src/main/java/com/seibel/lod/objects/PosToGenerateContainer.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -import com.seibel.lod.util.LevelPosUtil; - -/** - * Holds the levelPos that need to be generated. - * TODO is that correct? - * - * @author Leonardo Amato - * @version 9-27-2021 - */ -public class PosToGenerateContainer -{ - private final int playerPosX; - private final int playerPosZ; - private final byte farMinDetail; - private int nearSize; - private int farSize; - - // TODO what is the format of these two arrays? [detailLevel][4-children]? - private final int[][] nearPosToGenerate; - private final int[][] farPosToGenerate; - - - - - public PosToGenerateContainer(byte farMinDetail, int maxDataToGenerate, int playerPosX, int playerPosZ) - { - this.playerPosX = playerPosX; - this.playerPosZ = playerPosZ; - this.farMinDetail = farMinDetail; - nearSize = 0; - farSize = 0; - nearPosToGenerate = new int[maxDataToGenerate][4]; - farPosToGenerate = new int[maxDataToGenerate][4]; - } - - - - // TODO what is going on in this method? - public void addPosToGenerate(byte detailLevel, int posX, int posZ) - { - int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ); - int index; - - if (detailLevel >= farMinDetail) - { - // We are introducing a position in the far array - - if (farSize < farPosToGenerate.length) - farSize++; - - index = farSize - 1; - while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0) - { - farPosToGenerate[index][0] = farPosToGenerate[index - 1][0]; - farPosToGenerate[index][1] = farPosToGenerate[index - 1][1]; - farPosToGenerate[index][2] = farPosToGenerate[index - 1][2]; - farPosToGenerate[index][3] = farPosToGenerate[index - 1][3]; - index--; - } - - - if (index != farSize - 1 || farSize != farPosToGenerate.length) - { - farPosToGenerate[index][0] = detailLevel + 1; - farPosToGenerate[index][1] = posX; - farPosToGenerate[index][2] = posZ; - farPosToGenerate[index][3] = distance; - } - } - else - { - //We are introducing a position in the near array - - if (nearSize < nearPosToGenerate.length) - nearSize++; - - index = nearSize - 1; - while (index > 0 && LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) <= 0) - { - nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0]; - nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1]; - nearPosToGenerate[index][2] = nearPosToGenerate[index - 1][2]; - nearPosToGenerate[index][3] = nearPosToGenerate[index - 1][3]; - index--; - } - - - if (index != nearSize - 1 || nearSize != nearPosToGenerate.length) - { - nearPosToGenerate[index][0] = detailLevel + 1; - nearPosToGenerate[index][1] = posX; - nearPosToGenerate[index][2] = posZ; - nearPosToGenerate[index][3] = distance; - } - } - } - - - - public int getNumberOfPos() - { - return nearSize + farSize; - } - - public int getNumberOfNearPos() - { - return nearSize; - } - - public int getNumberOfFarPos() - { - return farSize; - } - - // TODO what does getNth mean? could the name be more descriptive or is it just a index? - public int getNthDetail(int n, boolean near) - { - if (near) - return nearPosToGenerate[n][0]; - else - return farPosToGenerate[n][0]; - } - - public int getNthPosX(int n, boolean near) - { - if (near) - return nearPosToGenerate[n][1]; - else - return farPosToGenerate[n][1]; - } - - public int getNthPosZ(int n, boolean near) - { - if (near) - return nearPosToGenerate[n][2]; - else - return farPosToGenerate[n][2]; - } - - public int getNthGeneration(int n, boolean near) - { - if (near) - return nearPosToGenerate[n][3]; - else - return farPosToGenerate[n][3]; - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append('\n'); - builder.append('\n'); - builder.append('\n'); - builder.append("near pos to generate"); - builder.append('\n'); - for (int[] ints : nearPosToGenerate) - { - if (ints[0] == 0) - break; - builder.append(ints[0] - 1); - builder.append(" "); - builder.append(ints[1]); - builder.append(" "); - builder.append(ints[2]); - builder.append(" "); - builder.append(ints[3]); - builder.append('\n'); - } - builder.append('\n'); - - builder.append("far pos to generate"); - builder.append('\n'); - for (int[] ints : farPosToGenerate) - { - if (ints[0] == 0) - break; - builder.append(ints[0] - 1); - builder.append(" "); - builder.append(ints[1]); - builder.append(" "); - builder.append(ints[2]); - builder.append(" "); - builder.append(ints[3]); - builder.append('\n'); - } - return builder.toString(); - } -} diff --git a/src/main/java/com/seibel/lod/objects/PosToRenderContainer.java b/src/main/java/com/seibel/lod/objects/PosToRenderContainer.java deleted file mode 100644 index ce01fb601..000000000 --- a/src/main/java/com/seibel/lod/objects/PosToRenderContainer.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -import java.util.Arrays; - -import com.seibel.lod.proxy.ClientProxy; -import com.seibel.lod.util.LevelPosUtil; -import com.seibel.lod.util.LodUtil; - -/** - * @author Leonardo Amato - * @version 9-18-2021 - */ -public class PosToRenderContainer -{ - public byte minDetail; - private int regionPosX; - private int regionPosZ; - private int numberOfPosToRender; - private int[] posToRender; - /*TODO this population matrix could be converted to boolean to improve memory use - * no since bools are stored as bytes anyway - cola*/ - private byte[][] population; - - public PosToRenderContainer(byte minDetail, int regionPosX, int regionPosZ) - { - this.minDetail = minDetail; - this.numberOfPosToRender = 0; - this.regionPosX = regionPosX; - this.regionPosZ = regionPosZ; - int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail); - posToRender = new int[size * size * 3]; - population = new byte[size][size]; - } - - public void addPosToRender(byte detailLevel, int posX, int posZ) - { - // When rapidly changing dimensions the bufferBuilder can cause this, - // James isn't sure why, but this will prevent an exception at - // the very least (while stilling logging the problem). - if (numberOfPosToRender >= posToRender.length) - { - // This is might be due to dimensions having a different width - // when first loading in - ClientProxy.LOGGER.error("Unable to addPosToRender. numberOfPosToRender [" + numberOfPosToRender + "] detailLevel [" + detailLevel + "] Pos [" + posX + "," + posZ + "]"); - numberOfPosToRender++; // incrementing so we can see how many pos over the limit we would go - return; - } - - //if(numberOfPosToRender >= posToRender.length) - // posToRender = Arrays.copyOf(posToRender, posToRender.length*2); - posToRender[numberOfPosToRender * 3] = detailLevel; - posToRender[numberOfPosToRender * 3 + 1] = posX; - posToRender[numberOfPosToRender * 3 + 2] = posZ; - numberOfPosToRender++; - population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posX, minDetail))] - [LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posZ, minDetail))] = (byte) (detailLevel + 1); - } - - public boolean contains(byte detailLevel, int posX, int posZ) - { - if (LevelPosUtil.getRegion(detailLevel, posX) == regionPosX && LevelPosUtil.getRegion(detailLevel, posZ) == regionPosZ) - return (population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posX, minDetail))] - [LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posZ, minDetail))] == (detailLevel + 1)); - else - return false; - } - - public void clear(byte minDetail, int regionPosX, int regionPosZ) - { - this.numberOfPosToRender = 0; - this.regionPosX = regionPosX; - this.regionPosZ = regionPosZ; - if (this.minDetail == minDetail) - { - Arrays.fill(posToRender, 0); - for (byte[] bytes : population) - Arrays.fill(bytes, (byte) 0); - } - else - { - this.minDetail = minDetail; - int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail); - posToRender = new int[size * size * 3]; - population = new byte[size][size]; - } - } - - public int getNumberOfPos() - { - return numberOfPosToRender; - } - - public byte getNthDetailLevel(int n) - { - return (byte) posToRender[n * 3]; - } - - public int getNthPosX(int n) - { - return posToRender[n * 3 + 1]; - } - - public int getNthPosZ(int n) - { - return posToRender[n * 3 + 2]; - } - - @Override - public String toString() - { - - StringBuilder builder = new StringBuilder(); - builder.append("To render "); - builder.append(numberOfPosToRender); - builder.append('\n'); - for (int i = 0; i < numberOfPosToRender; i++) - { - builder.append(posToRender[i * 3]); - builder.append(" "); - builder.append(posToRender[i * 3 + 1]); - builder.append(" "); - builder.append(posToRender[i * 3 + 2]); - builder.append('\n'); - } - builder.append('\n'); - return builder.toString(); - } -} diff --git a/src/main/java/com/seibel/lod/objects/RegionPos.java b/src/main/java/com/seibel/lod/objects/RegionPos.java deleted file mode 100644 index 20ae78de5..000000000 --- a/src/main/java/com/seibel/lod/objects/RegionPos.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; - -/** - * This object is similar to ChunkPos or BlockPos. - * - * @author James Seibel - * @version 8-21-2021 - */ -public class RegionPos -{ - public int x; - public int z; - - - /** - * Default Constructor

- *

- * Sets x and z to 0 - */ - public RegionPos() - { - x = 0; - z = 0; - } - - /** simple constructor that sets x and z to new x and z. */ - public RegionPos(int newX, int newZ) - { - x = newX; - z = newZ; - } - - /** Converts from a BlockPos to a RegionPos */ - public RegionPos(BlockPosWrapper pos) - { - this(new ChunkPosWrapper(pos)); - } - - /** Converts from a ChunkPos to a RegionPos */ - public RegionPos(ChunkPosWrapper pos) - { - x = Math.floorDiv(pos.getX(), LodUtil.REGION_WIDTH_IN_CHUNKS); - z = Math.floorDiv(pos.getZ(), LodUtil.REGION_WIDTH_IN_CHUNKS); - } - - /** Returns the ChunkPos at the center of this region */ - public ChunkPosWrapper chunkPos() - { - return new ChunkPosWrapper( - (x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2, - (z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2); - } - - /** Returns the BlockPos at the center of this region */ - public BlockPosWrapper blockPos() - { - return chunkPos().getWorldPosition() - .offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2); - } - - - @Override - public String toString() - { - return "(" + x + "," + z + ")"; - } -} diff --git a/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java b/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java deleted file mode 100644 index 01cb97756..000000000 --- a/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.objects; - -import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.DetailDistanceUtil; -import com.seibel.lod.util.LevelPosUtil; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.util.ThreadMapUtil; - -/** - * - * @author Leonardo Amato - * @version ?? - */ -public class VerticalLevelContainer implements LevelContainer -{ - - public final byte detailLevel; - public final int size; - public final int maxVerticalData; - - public final long[] dataContainer; - - public VerticalLevelContainer(byte detailLevel) - { - this.detailLevel = detailLevel; - size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel); - dataContainer = new long[size * size * DetailDistanceUtil.getMaxVerticalData(detailLevel)]; - } - - @Override - public byte getDetailLevel() - { - return detailLevel; - } - - @Override - public void clear(int posX, int posZ) - { - posX = LevelPosUtil.getRegionModule(detailLevel, posX); - posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++) - { - dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = DataPointUtil.EMPTY_DATA; - } - } - - @Override - public boolean addData(long data, int posX, int posZ, int verticalIndex) - { - posX = LevelPosUtil.getRegionModule(detailLevel, posX); - posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data; - return true; - } - - @Override - public boolean addVerticalData(long[] data, int posX, int posZ) - { - posX = LevelPosUtil.getRegionModule(detailLevel, posX); - posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - for (int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++) - dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex] = data[verticalIndex]; - return true; - } - - @Override - public boolean addSingleData(long data, int posX, int posZ) - { - return addData(data, posX, posZ, 0); - } - - @Override - public long getData(int posX, int posZ, int verticalIndex) - { - posX = LevelPosUtil.getRegionModule(detailLevel, posX); - posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - return dataContainer[posX * size * maxVerticalData + posZ * maxVerticalData + verticalIndex]; - } - - @Override - public long getSingleData(int posX, int posZ) - { - return getData(posX, posZ, 0); - } - - @Override - public int getMaxVerticalData() - { - return maxVerticalData; - } - - public int getSize() - { - return size; - } - - @Override - public boolean doesItExist(int posX, int posZ) - { - posX = LevelPosUtil.getRegionModule(detailLevel, posX); - posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - return DataPointUtil.doesItExist(getSingleData(posX, posZ)); - } - - public VerticalLevelContainer(byte[] inputData) - { - int tempIndex; - int index = 0; - long newData; - detailLevel = inputData[index]; - index++; - maxVerticalData = inputData[index] & 0b01111111; - index++; - size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - int x = size * size * maxVerticalData; - this.dataContainer = new long[x]; - for (int i = 0; i < x; i++) - { - newData = 0; - for (tempIndex = 0; tempIndex < 8; tempIndex++) - newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex); - index += 8; - dataContainer[i] = newData; - } - } - - @Override - public LevelContainer expand() - { - return new VerticalLevelContainer((byte) (getDetailLevel() - 1)); - } - - @Override - public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ) - { - //We reset the array - long[] dataToMerge = ThreadMapUtil.getVerticalUpdateArray(detailLevel); - - int lowerMaxVertical = dataToMerge.length / 4; - int childPosX; - int childPosZ; - long[] data; - posX = LevelPosUtil.getRegionModule(detailLevel, posX); - posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - for (int x = 0; x <= 1; x++) - { - for (int z = 0; z <= 1; z++) - { - childPosX = 2 * posX + x; - childPosZ = 2 * posZ + z; - for (int verticalIndex = 0; verticalIndex < lowerMaxVertical; verticalIndex++) - dataToMerge[(z * 2 + x) * lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex); - } - } - data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getMaxVerticalData()); - - addVerticalData(data, posX, posZ); - } - - @Override - public byte[] toDataString() - { - int index = 0; - int x = size * size; - int tempIndex; - long current; - boolean allGenerated = true; - byte[] tempData = ThreadMapUtil.getSaveContainer(detailLevel); - - tempData[index] = detailLevel; - index++; - tempData[index] = (byte) maxVerticalData; - index++; - int j; - for (int i = 0; i < x; i++) - { - for (j = 0; j < maxVerticalData; j++) - { - current = dataContainer[i * maxVerticalData + j]; - for (tempIndex = 0; tempIndex < 8; tempIndex++) - tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex)); - index += 8; - } - if(!DataPointUtil.doesItExist(dataContainer[i])) - allGenerated = false; - } - if (allGenerated) - tempData[1] |= 0b10000000; - return tempData; - } - - @Override - @SuppressWarnings("unused") - public String toString() - { - /* - StringBuilder stringBuilder = new StringBuilder(); - int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - stringBuilder.append(detailLevel); - stringBuilder.append(DATA_DELIMITER); - for (int x = 0; x < size; x++) - { - for (int z = 0; z < size; z++) - { - //Converting the dataToHex - stringBuilder.append(Long.toHexString(dataContainer[x][z][0])); - stringBuilder.append(DATA_DELIMITER); - } - } - return stringBuilder.toString(); - */ - return " "; - } - - @Override - public int getMaxNumberOfLods() - { - return size * size * getMaxVerticalData(); - } -} diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java deleted file mode 100644 index dea66e721..000000000 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.proxy; - -import com.seibel.lod.wrappers.Chunk.ChunkWrapper; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.lwjgl.glfw.GLFW; - -import com.mojang.blaze3d.matrix.MatrixStack; -import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder; -import com.seibel.lod.builders.lodBuilding.LodBuilder; -import com.seibel.lod.builders.worldGeneration.LodGenWorker; -import com.seibel.lod.builders.worldGeneration.LodWorldGenerator; -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.LodWorld; -import com.seibel.lod.objects.RegionPos; -import com.seibel.lod.render.LodRenderer; -import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.DetailDistanceUtil; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.util.ThreadMapUtil; -import com.seibel.lod.wrappers.MinecraftWrapper; - -import net.minecraft.profiler.IProfiler; -import net.minecraft.util.text.StringTextComponent; -import net.minecraftforge.client.event.InputEvent; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.event.world.ChunkEvent; -import net.minecraftforge.event.world.WorldEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; - -/** - * This handles all events sent to the client, - * and is the starting point for most of the mod. - * @author James_Seibel - * @version 10-23-2021 - */ -public class ClientProxy -{ - public static final Logger LOGGER = LogManager.getLogger("LOD"); - - /** - * there is some setup that should only happen once, - * once this is true that setup has completed - */ - private boolean firstTimeSetupComplete = false; - - private static final LodWorld lodWorld = new LodWorld(); - private static final LodBuilder lodBuilder = new LodBuilder(); - private static final LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(); - private static LodRenderer renderer = new LodRenderer(lodBufferBuilder); - private static final LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE; - - private boolean configOverrideReminderPrinted = false; - - private final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; - - - /** This is used to determine if the LODs should be regenerated */ - public static int previousChunkRenderDistance = 0; - /** This is used to determine if the LODs should be regenerated */ - public static int previousLodRenderDistance = 0; - - /** - * can be set if we want to recalculate variables related - * to the LOD view distance - */ - private boolean recalculateWidths = false; - - public ClientProxy() - { - - } - - - //==============// - // render event // - //==============// - - /** Do any setup that is required to draw LODs and then tell the LodRenderer to draw. */ - public void renderLods(MatrixStack mcMatrixStack, float partialTicks) - { - // comment out when creating a release - // applyConfigOverrides(); - - // clear any out of date objects - mc.clearFrameObjectCache(); - - try - { - // only run the first time setup once - if (!firstTimeSetupComplete) - firstFrameSetup(); - - - if (mc.getPlayer() == null || lodWorld.getIsWorldNotLoaded()) - return; - - LodDimension lodDim = lodWorld.getLodDimension(mc.getCurrentDimension()); - if (lodDim == null) - return; - - DetailDistanceUtil.updateSettings(); - viewDistanceChangedEvent(); - playerMoveEvent(lodDim); - - lodDim.cutRegionNodesAsync((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ()); - lodDim.expandOrLoadRegionsAsync((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ()); - - - // Note to self: - // if "unspecified" shows up in the pie chart, it is - // possibly because the amount of time between sections - // is too small for the profiler to measure - IProfiler profiler = mc.getProfiler(); - profiler.pop(); // get out of "terrain" - profiler.push("LOD"); - - renderer.drawLODs(lodDim, mcMatrixStack, partialTicks, mc.getProfiler()); - - profiler.pop(); // end LOD - profiler.push("terrain"); // go back into "terrain" - - - // these can't be set until after the buffers are built (in renderer.drawLODs) - // otherwise the buffers may be set to the wrong size, or not changed at all - previousChunkRenderDistance = mc.getRenderDistance(); - previousLodRenderDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(); - } - catch (Exception e) - { - LOGGER.error("client proxy: " + e.getMessage()); - e.printStackTrace(); - } - } - - /** used in a development environment to change settings on the fly */ - private void applyConfigOverrides() - { - // remind the developer(s) that the config override is active - if (!configOverrideReminderPrinted) - { -// mc.getPlayer().sendMessage(new StringTextComponent("LOD experimental build 1.5.1"), mc.getPlayer().getUUID()); -// mc.getPlayer().sendMessage(new StringTextComponent("Here be dragons!"), mc.getPlayer().getUUID()); - - mc.getPlayer().sendMessage(new StringTextComponent("Debug settings enabled!"), mc.getPlayer().getUUID()); - configOverrideReminderPrinted = true; - } - -// LodConfig.CLIENT.graphics.drawResolution.set(HorizontalResolution.BLOCK); -// LodConfig.CLIENT.worldGenerator.generationResolution.set(HorizontalResolution.BLOCK); - // requires a world restart? -// LodConfig.CLIENT.worldGenerator.lodQualityMode.set(VerticalQuality.VOXEL); - -// LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.set(FogDistance.FAR); -// LodConfig.CLIENT.graphics.fogQualityOption.fogDrawOverride.set(FogDrawOverride.FANCY); -// LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.set(true); -// LodConfig.CLIENT.graphics.shadingMode.set(ShadingMode.DARKEN_SIDES); - -// LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.set(VanillaOverdraw.DYNAMIC); - -// LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.BUFFER_STORAGE); - -// LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); -// LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(128); -// LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); -// LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(false); - -// LodConfig.CLIENT.buffers.rebuildTimes.set(BufferRebuildTimes.FREQUENT); - - LodConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.set(true); -// LodConfig.CLIENT.debugging.debugMode.set(DebugMode.SHOW_DETAIL); - } - - - //==============// - // forge events // - //==============// - - @SubscribeEvent - public void serverTickEvent(TickEvent.ServerTickEvent event) - { - if (mc.getPlayer() == null || lodWorld.getIsWorldNotLoaded()) - return; - - LodDimension lodDim = lodWorld.getLodDimension(mc.getPlayer().level.dimensionType()); - if (lodDim == null) - return; - - lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder); - } - - @SubscribeEvent - public void chunkLoadEvent(ChunkEvent.Load event) - { - lodBuilder.generateLodNodeAsync(new ChunkWrapper(event.getChunk()), lodWorld, event.getWorld(), DistanceGenerationMode.SERVER); - } - - @SubscribeEvent - public void worldSaveEvent(WorldEvent.Save event) - { - lodWorld.saveAllDimensions(); - } - - /** This is also called when a new dimension loads */ - @SubscribeEvent - public void worldLoadEvent(WorldEvent.Load event) - { - DataPointUtil.worldHeight = event.getWorld().getHeight(); - //LodNodeGenWorker.restartExecutorService(); - //ThreadMapUtil.clearMaps(); - - // the player just loaded a new world/dimension - lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld())); - - // make sure the correct LODs are being rendered - // (if this isn't done the previous world's LODs may be drawn) - renderer.regenerateLODsNextFrame(); - } - - @SubscribeEvent - public void worldUnloadEvent(WorldEvent.Unload event) - { - // the player just unloaded a world/dimension - ThreadMapUtil.clearMaps(); - - - if (mc.getConnection().getLevel() == null) - { - // the player just left the server - - // if this isn't done unfinished tasks may be left in the queue - // preventing new LodChunks form being generated - //LodNodeGenWorker.restartExecutorService(); - //ThreadMapUtil.clearMaps(); - - LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0); - lodWorld.deselectWorld(); - - - // hopefully this should reduce issues related to the buffer builder - // breaking when changing worlds. - renderer.destroyBuffers(); - recalculateWidths = true; - renderer = new LodRenderer(lodBufferBuilder); - - - // make sure the nilled objects are freed. - // (this prevents an out of memory error when - // changing worlds) - System.gc(); - } - } - - @SubscribeEvent - public void blockChangeEvent(BlockEvent event) - { - if (event.getClass() == BlockEvent.BreakEvent.class || - event.getClass() == BlockEvent.EntityPlaceEvent.class || - event.getClass() == BlockEvent.EntityMultiPlaceEvent.class || - event.getClass() == BlockEvent.FluidPlaceBlockEvent.class || - event.getClass() == BlockEvent.PortalSpawnEvent.class) - { - // recreate the LOD where the blocks were changed - lodBuilder.generateLodNodeAsync(new ChunkWrapper(event.getWorld().getChunk(event.getPos())), lodWorld, event.getWorld()); - } - } - - @SubscribeEvent - public void onKeyInput(InputEvent.KeyInputEvent event) - { - if (LodConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.get() - && event.getKey() == GLFW.GLFW_KEY_F4 && event.getAction() == GLFW.GLFW_PRESS) - { - LodConfig.CLIENT.advancedModOptions.debugging.debugMode.set(LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get().getNext()); - } - - if (LodConfig.CLIENT.advancedModOptions.debugging.enableDebugKeybindings.get() - && event.getKey() == GLFW.GLFW_KEY_F6 && event.getAction() == GLFW.GLFW_PRESS) - { - LodConfig.CLIENT.advancedModOptions.debugging.drawLods.set(!LodConfig.CLIENT.advancedModOptions.debugging.drawLods.get()); - } - } - - - - - //============// - // LOD events // - //============// - - /** Re-centers the given LodDimension if it needs to be. */ - private void playerMoveEvent(LodDimension lodDim) - { - // make sure the dimension is centered - RegionPos playerRegionPos = new RegionPos(mc.getPlayerBlockPos()); - RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterRegionPosX(), playerRegionPos.z - lodDim.getCenterRegionPosZ()); - if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0) - { - lodWorld.saveAllDimensions(); - lodDim.move(worldRegionOffset); - //LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ()); - } - } - - - /** Re-sizes all LodDimensions if they need to be. */ - private void viewDistanceChangedEvent() - { - // calculate how wide the dimension(s) should be in regions - int chunksWide; - if (mc.getClientLevel().dimensionType().hasCeiling()) - chunksWide = Math.min(LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1; - else - chunksWide = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * 2 + 1; - - int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS); - // make sure we have an odd number of regions - newWidth += (newWidth & 1) == 0 ? 1 : 2; - - // do the dimensions need to change in size? - if (lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths) - { - lodWorld.saveAllDimensions(); - - // update the dimensions to fit the new width - lodWorld.resizeDimensionRegionWidth(newWidth); - lodBuilder.defaultDimensionWidthInRegions = newWidth; - renderer.setupBuffers(lodWorld.getLodDimension(mc.getClientLevel().dimensionType())); - - recalculateWidths = false; - //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); - } - DetailDistanceUtil.updateSettings(); - } - - - /** This event is called once during the first frame Minecraft renders in the world. */ - public void firstFrameSetup() - { - // make sure the GlProxy is created before the LodBufferBuilder needs it - GlProxy.getInstance(); - - firstTimeSetupComplete = true; - } - - /** this method reset some static data every time we change world */ - private void resetMod() - { - ThreadMapUtil.clearMaps(); - LodGenWorker.restartExecutorService(); - - } - - - - - //================// - // public getters // - //================// - - public static LodWorld getLodWorld() - { - return lodWorld; - } - - public static LodBuilder getLodBuilder() - { - return lodBuilder; - } - - public static LodRenderer getRenderer() - { - return renderer; - } -} diff --git a/src/main/java/com/seibel/lod/proxy/GlProxy.java b/src/main/java/com/seibel/lod/proxy/GlProxy.java deleted file mode 100644 index cfecd809c..000000000 --- a/src/main/java/com/seibel/lod/proxy/GlProxy.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.proxy; - -import org.lwjgl.glfw.GLFW; -import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GLCapabilities; - -import com.mojang.blaze3d.systems.RenderSystem; -import com.seibel.lod.ModInfo; -import com.seibel.lod.enums.GlProxyContext; -import com.seibel.lod.wrappers.MinecraftWrapper; - -/** - * A singleton that holds references to different openGL contexts - * and GPU capabilities. - * - *

- * Helpful OpenGL resources:

- *

- * https://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
- * https://learnopengl.com/Advanced-OpenGL/Advanced-Data
- * https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one

- * - * @author James Seibel - * @version 10-23-2021 - */ -public class GlProxy -{ - private static GlProxy instance = null; - - private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; - - /** Minecraft's GLFW window */ - public final long minecraftGlContext; - /** Minecraft's GL context */ - public final GLCapabilities minecraftGlCapabilities; - - /** the LodBuilder's GLFW window */ - public final long lodBuilderGlContext; - /** the LodBuilder's GL context */ - public final GLCapabilities lodBuilderGlCapabilities; - - /** - * This is just used for debugging, hopefully it can be removed once - * the context switching is more stable. - */ - public Thread lodBuilderOwnerThread = null; - - /** Does this computer's GPU support fancy fog? */ - public final boolean fancyFogAvailable; - - /** Requires OpenGL 4.5, and offers the best buffer uploading */ - public final boolean bufferStorageSupported; - - /** Requires OpenGL 3.0 */ - public final boolean mapBufferRangeSupported; - - - private GlProxy() - { - ClientProxy.LOGGER.error("Creating " + GlProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error."); - - // getting Minecraft's context has to be done on the render thread, - // where the GL context is - if (!RenderSystem.isOnRenderThread()) - throw new IllegalStateException(GlProxy.class.getSimpleName() + " was created outside the render thread!"); - - - - //============================// - // create the builder context // - //============================// - - // get Minecraft's context - minecraftGlContext = GLFW.glfwGetCurrentContext(); - minecraftGlCapabilities = GL.getCapabilities(); - - // create the LodBuilder's context - - // Hopefully this shouldn't cause any issues with other mods that need custom contexts - // (although the number that do should be relatively few) - GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); - - // create an invisible window to hold the context - lodBuilderGlContext = GLFW.glfwCreateWindow(640, 480, "LOD window", 0L, minecraftGlContext); - GLFW.glfwMakeContextCurrent(lodBuilderGlContext); - lodBuilderGlCapabilities = GL.createCapabilities(); - - // Since this is called on the render thread, make sure the Minecraft context is used in the end - GLFW.glfwMakeContextCurrent(minecraftGlContext); - GL.setCapabilities(minecraftGlCapabilities); - - - - - //==============================// - // determine the OpenGL version // - //==============================// - - bufferStorageSupported = minecraftGlCapabilities.OpenGL45; - mapBufferRangeSupported = minecraftGlCapabilities.OpenGL30; - - if (!minecraftGlCapabilities.OpenGL15) - { - String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 1.5 or greater."; - mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 1.5 or greater.")); - } - - if (!bufferStorageSupported) - { - String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5"; - - ClientProxy.LOGGER.error("This GPU doesn't support OpenGL 4.5, falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance."); - } - - - - - - //==================================// - // get any GPU related capabilities // - //==================================// - - // see if this GPU can run fancy fog - fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance; - - if (!fancyFogAvailable) - ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that the fancy fog option will not be available."); - - - - - // GlProxy creation success - ClientProxy.LOGGER.error(GlProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day."); - } - - - - - /** - * A wrapper function to make switching contexts easier.
- * Does nothing if the calling thread is already using newContext. - */ - public void setGlContext(GlProxyContext newContext) - { - GlProxyContext currentContext = getGlContext(); - - // we don't have to change the context, we're already there. - if (currentContext == newContext) - return; - - - long contextPointer = 0L; - GLCapabilities newGlCapabilities = null; - - // get the pointer(s) for this context - switch (newContext) - { - case LOD_BUILDER: - contextPointer = lodBuilderGlContext; - newGlCapabilities = lodBuilderGlCapabilities; - break; - - case MINECRAFT: - contextPointer = minecraftGlContext; - newGlCapabilities = minecraftGlCapabilities; - break; - - default: // default should never happen, it is just here to make the compiler happy - case NONE: - // 0L is equivalent to null - break; - } - - - GLFW.glfwMakeContextCurrent(contextPointer); - GL.setCapabilities(newGlCapabilities); - - - - // used for debugging - if (newContext == GlProxyContext.LOD_BUILDER) - lodBuilderOwnerThread = Thread.currentThread(); - else if (newContext == GlProxyContext.NONE && currentContext == GlProxyContext.LOD_BUILDER) - lodBuilderOwnerThread = null; - } - - - - - - /** Returns this thread's OpenGL context. */ - public GlProxyContext getGlContext() - { - long currentContext = GLFW.glfwGetCurrentContext(); - - - if (currentContext == lodBuilderGlContext) - return GlProxyContext.LOD_BUILDER; - else if (currentContext == minecraftGlContext) - return GlProxyContext.MINECRAFT; - else if (currentContext == 0L) - return GlProxyContext.NONE; - else - // hopefully this shouldn't happen, but - // at least now we will be notified if it does happen - throw new IllegalStateException(Thread.currentThread().getName() + " has a unknown OpenGl context: [" + currentContext + "]. Minecraft context [" + minecraftGlContext + "], LodBuilder context [" + lodBuilderGlContext + "], no context [0]."); - } - - - public static GlProxy getInstance() - { - if (instance == null) - instance = new GlProxy(); - - return instance; - } - - - - - - - - -} diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java deleted file mode 100644 index 2164e349b..000000000 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ /dev/null @@ -1,959 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.render; - -import com.mojang.blaze3d.matrix.MatrixStack; -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; -import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder; -import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder.VertexBuffersAndOffset; -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.*; -import com.seibel.lod.handlers.ReflectionHandler; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.NearFarFogSettings; -import com.seibel.lod.objects.RegionPos; -import com.seibel.lod.proxy.ClientProxy; -import com.seibel.lod.proxy.GlProxy; -import com.seibel.lod.util.DetailDistanceUtil; -import com.seibel.lod.util.LevelPosUtil; -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; -import com.seibel.lod.wrappers.MinecraftWrapper; -import net.minecraft.client.renderer.ActiveRenderInfo; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.vertex.VertexBuffer; -import net.minecraft.potion.Effects; -import net.minecraft.profiler.IProfiler; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.vector.Matrix4f; -import net.minecraft.util.math.vector.Vector3d; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL15C; -import org.lwjgl.opengl.NVFogDistance; - -import java.util.HashSet; - - -/** - * This is where all the magic happens.
- * This is where LODs are draw to the world. - * @author James Seibel - * @version 10-25-2021 - */ -public class LodRenderer -{ - /** - * this is the light used when rendering the LODs, - * it should be something different from what is used by Minecraft - */ - private static final int LOD_GL_LIGHT_NUMBER = GL15.GL_LIGHT2; - - /** - * If true the LODs colors will be replaced with - * a checkerboard, this can be used for debugging. - */ - public DebugMode previousDebugMode = DebugMode.OFF; - - private final MinecraftWrapper mc; - private final GameRenderer gameRender; - private IProfiler profiler; - private int farPlaneBlockDistance; - - - /** This is used to generate the buildable buffers */ - private final LodBufferBuilder lodBufferBuilder; - - /** Each VertexBuffer represents 1 region */ - private VertexBuffer[][][] vbos; - /** - * the OpenGL IDs for the vbos of the same indices. - * These have to be separate because we can't override the - * buffers in the VBOs (and we don't want to) - */ - private int[][][] storageBufferIds; - - private ChunkPosWrapper vbosCenter = new ChunkPosWrapper(0, 0); - - - /** This is used to determine if the LODs should be regenerated */ - private int[] previousPos = new int[] { 0, 0, 0 }; - - // these variables are used to determine if the buffers should be rebuilt - private float prevSkyBrightness = 0; - private double prevBrightness = 0; - private int prevRenderDistance = 0; - private long prevPlayerPosTime = 0; - private long prevVanillaChunkTime = 0; - private long prevChunkTime = 0; - - - /** This is used to determine if the LODs should be regenerated */ - private FogDistance prevFogDistance = FogDistance.NEAR_AND_FAR; - - /** - * if this is true the LOD buffers should be regenerated, - * provided they aren't already being regenerated. - */ - private volatile boolean partialRegen = false; - private volatile boolean fullRegen = true; - - /** - * This HashSet contains every chunk that Vanilla Minecraft - * is going to render - */ - public boolean[][] vanillaRenderedChunks; - public boolean vanillaRenderedChunksChanged; - public boolean vanillaRenderedChunksEmptySkip = false; - public int vanillaBlockRenderedDistance; - - final boolean vivecraftDetected = ReflectionHandler.INSTANCE.detectVivecraft(); - - - - public LodRenderer(LodBufferBuilder newLodNodeBufferBuilder) - { - mc = MinecraftWrapper.INSTANCE; - gameRender = mc.getGameRenderer(); - - lodBufferBuilder = newLodNodeBufferBuilder; - } - - - - - - - - /** - * Besides drawing the LODs this method also starts - * the async process of generating the Buffers that hold those LODs. - * @param lodDim The dimension to draw, if null doesn't replace the current dimension. - * @param mcMatrixStack This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method - * @param partialTicks how far into the current tick this method was called. - */ - @SuppressWarnings("deprecation") - public void drawLODs(LodDimension lodDim, MatrixStack mcMatrixStack, float partialTicks, IProfiler newProfiler) - { - //=================================// - // determine if LODs should render // - //=================================// - - if (lodDim == null) - { - // if there aren't any loaded LodChunks - // don't try drawing anything - return; - } - - if (mc.getPlayer().getActiveEffectsMap().get(Effects.BLINDNESS) != null) - { - // if the player is blind don't render LODs, - // and don't change minecraft's fog - // which blindness relies on. - return; - } - - - - - - //===============// - // initial setup // - //===============// - - profiler = newProfiler; - profiler.push("LOD setup"); - - - // TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead) - // starting here... - determineIfLodsShouldRegenerate(lodDim, partialTicks); - - //=================// - // create the LODs // - //=================// - - // only regenerate the LODs if: - // 1. we want to regenerate LODs - // 2. we aren't already regenerating the LODs - // 3. we aren't waiting for the build and draw buffers to swap - // (this is to prevent thread conflicts) - if (LodConfig.CLIENT.advancedModOptions.debugging.drawLods.get()) - { - if (lodBufferBuilder.buildableBuffers == null) - lodBufferBuilder.setupBuffers(lodDim); - - if ((partialRegen || fullRegen) && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvailable()) - { - // generate the LODs on a separate thread to prevent stuttering or freezing - lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayerBlockPos(), fullRegen); - - // the regen process has been started, - // it will be done when lodBufferBuilder.newBuffersAvailable() - // is true - fullRegen = false; - partialRegen = false; - } - } - else - lodBufferBuilder.destroyBuffers(); - - // TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead) - // ...ending here - - if (lodBufferBuilder.newBuffersAvailable()) - { - swapBuffers(); - } - - - //===========================// - // GL settings for rendering // - //===========================// - - // set the required open GL settings - - if (LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get() == DebugMode.SHOW_DETAIL_WIREFRAME) - GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_LINE); - else - GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL); - - GL15.glDisable(GL15.GL_TEXTURE_2D); - GL15.glEnable(GL15.GL_CULL_FACE); - GL15.glEnable(GL15.GL_COLOR_MATERIAL); - GL15.glEnable(GL15.GL_DEPTH_TEST); - - // enable transparent rendering - GL15.glBlendFunc(GL15.GL_SRC_ALPHA, GL15.GL_ONE_MINUS_SRC_ALPHA); - GL15.glEnable(GL15.GL_BLEND); - - // disable the lights Minecraft uses - GL15.glDisable(GL15.GL_LIGHT0); - GL15.glDisable(GL15.GL_LIGHT1); - - // get the default projection matrix, so we can - // reset it after drawing the LODs - float[] mcProjMatrixRaw = new float[16]; - GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw); - Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw); - // OpenGl outputs their matrices in col,row form instead of row,col - // (or maybe vice versa I have no idea :P) - mcProjectionMatrix.transpose(); - - Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcMatrixStack, partialTicks); - vanillaBlockRenderedDistance = mc.getRenderDistance() * LodUtil.CHUNK_WIDTH; - // required for setupFog and setupProjectionMatrix - if (mc.getClientLevel().dimensionType().hasCeiling()) - farPlaneBlockDistance = Math.min(LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH; - else - farPlaneBlockDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH; - - setupProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks); - - // commented out until we can add shaders to handle lighting - //setupLighting(lodDim, partialTicks); - - NearFarFogSettings fogSettings = determineFogSettings(); - - // determine the current fog settings, so they can be - // reset after drawing the LODs - float defaultFogStartDist = GL15.glGetFloat(GL15.GL_FOG_START); - float defaultFogEndDist = GL15.glGetFloat(GL15.GL_FOG_END); - int defaultFogMode = GL15.glGetInteger(GL15.GL_FOG_MODE); - int defaultFogDistance = GlProxy.getInstance().fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1; - - //===========// - // rendering // - //===========// - - profiler.popPush("LOD draw"); - - if (vbos != null && LodConfig.CLIENT.advancedModOptions.debugging.drawLods.get()) - { - ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera(); - Vector3d cameraDir = new Vector3d(renderInfo.getLookVector()); - - boolean cullingDisabled = LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get(); - boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && GlProxy.getInstance().bufferStorageSupported; - - // used to determine what type of fog to render - int halfWidth = vbos.length / 2; - int quarterWidth = vbos.length / 4; - - // where the center of the built buffers is (needed when culling regions) - RegionPos vboCenterRegionPos = new RegionPos(vbosCenter); - - - for (int x = 0; x < vbos.length; x++) - { - for (int z = 0; z < vbos.length; z++) - { - RegionPos vboPos = new RegionPos( - x + vboCenterRegionPos.x - (lodDim.getWidth() / 2), - z + vboCenterRegionPos.z - (lodDim.getWidth() / 2)); - - if (cullingDisabled || RenderUtil.isRegionInViewFrustum(renderInfo.getBlockPosition(), cameraDir, vboPos.blockPos())) - { - if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth) - && (z > halfWidth - quarterWidth && z < halfWidth + quarterWidth)) - setupFog(fogSettings.near.distance, fogSettings.near.quality); - else - setupFog(fogSettings.far.distance, fogSettings.far.quality); - - - if (storageBufferIds != null && renderBufferStorage) - for (int i = 0; i < storageBufferIds[x][z].length; i++) - drawStorageBuffer(vbos[x][z][i], storageBufferIds[x][z][i], modelViewMatrix); - else - for (int i = 0; i < vbos[x][z].length; i++) - drawVertexBuffer(vbos[x][z][i], modelViewMatrix); - } - } - } - } - - - //=========// - // cleanup // - //=========// - - profiler.popPush("LOD cleanup"); - - GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL); - GL15.glEnable(GL15.GL_TEXTURE_2D); - GL15.glDisable(LOD_GL_LIGHT_NUMBER); - GL15.glDisable(GL15.GL_BLEND); - // re-enable the lights Minecraft uses - GL15.glEnable(GL15.GL_LIGHT0); - GL15.glEnable(GL15.GL_LIGHT1); - RenderSystem.disableLighting(); - - // reset the fog settings so the normal chunks - // will be drawn correctly - cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance); - - // reset the projection matrix so anything drawn after - // the LODs will use the correct projection matrix - gameRender.resetProjectionMatrix(mcProjectionMatrix); - - // clear the depth buffer so anything drawn is drawn - // over the LODs - GL15.glClear(GL15.GL_DEPTH_BUFFER_BIT); - - - // end of internal LOD profiling - profiler.pop(); - } - - - /** This is where the actual drawing happens. */ - private void drawStorageBuffer(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix) - { - if (vbo == null) - return; - - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId); - // 0L is the starting pointer - LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L); - - vbo.draw(modelViewMatrix, GL15.GL_QUADS); - - GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - LodUtil.LOD_VERTEX_FORMAT.clearBufferState(); - } - - - /** This is where the actual drawing happens. */ - private void drawVertexBuffer(VertexBuffer vbo, Matrix4f modelViewMatrix) - { - if (vbo == null) - return; - - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); - // 0L is the starting pointer - LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L); - - vbo.draw(modelViewMatrix, GL15.GL_QUADS); - - GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - LodUtil.LOD_VERTEX_FORMAT.clearBufferState(); - } - - - - - //=================// - // Setup Functions // - //=================// - - @SuppressWarnings("deprecation") - private void setupFog(FogDistance fogDistance, FogQuality fogQuality) - { - if (fogQuality == FogQuality.OFF) - { - GL15.glDisable(GL15.GL_FOG); - return; - } - - if (fogDistance == FogDistance.NEAR_AND_FAR) - { - throw new IllegalArgumentException("setupFog doesn't accept the NEAR_AND_FAR fog distance."); - } - - // determine the fog distance mode to use - int glFogDistanceMode; - if (fogQuality == FogQuality.FANCY) - { - // fancy fog (fragment distance based fog) - glFogDistanceMode = NVFogDistance.GL_EYE_RADIAL_NV; - } - else - { - // fast fog (frustum distance based fog) - glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV; - } - - // the multipliers are percentages - // of the regular view distance. - if (fogDistance == FogDistance.FAR) - { - // the reason that I wrote fogEnd then fogStart backwards - // is because we are using fog backwards to how - // it is normally used, with it hiding near objects - // instead of far objects. - - if (fogQuality == FogQuality.FANCY) - { - // for more realistic fog when using FAR - if (LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get() == FogDistance.NEAR_AND_FAR) - RenderSystem.fogStart(farPlaneBlockDistance * 1.6f * 0.9f); - else - RenderSystem.fogStart(Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f)); - RenderSystem.fogEnd(farPlaneBlockDistance * 1.6f); - } - else if (fogQuality == FogQuality.FAST) - { - // for the far fog of the normal chunks - // to start right where the LODs' end use: - // end = 0.8f, start = 1.5f - RenderSystem.fogStart(farPlaneBlockDistance * 0.75f); - RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f); - } - } - else if (fogDistance == FogDistance.NEAR) - { - if (fogQuality == FogQuality.FANCY) - { - RenderSystem.fogEnd(vanillaBlockRenderedDistance * 1.41f); - RenderSystem.fogStart(vanillaBlockRenderedDistance * 1.6f); - } - else if (fogQuality == FogQuality.FAST) - { - RenderSystem.fogEnd(vanillaBlockRenderedDistance * 1.0f); - RenderSystem.fogStart(vanillaBlockRenderedDistance * 1.5f); - } - } - - GL15.glEnable(GL15.GL_FOG); - RenderSystem.enableFog(); - RenderSystem.setupNvFogDistance(); - RenderSystem.fogMode(GlStateManager.FogMode.LINEAR); - - if (GlProxy.getInstance().fancyFogAvailable) - GL15.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode); - } - - /** - * Revert any changes that were made to the fog - * and sets up the fog for Minecraft. - */ - @SuppressWarnings("deprecation") - private void cleanupFog(NearFarFogSettings fogSettings, - float defaultFogStartDist, float defaultFogEndDist, - int defaultFogMode, int defaultFogDistance) - { - RenderSystem.fogStart(defaultFogStartDist); - RenderSystem.fogEnd(defaultFogEndDist); - RenderSystem.fogMode(defaultFogMode); - - // this setting is only valid if the GPU supports fancy fog - if (GlProxy.getInstance().fancyFogAvailable) - GL15.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, defaultFogDistance); - - // disable fog if Minecraft wasn't rendering fog - // or we want it disabled - if (!fogSettings.vanillaIsRenderingFog - || LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.get()) - { - // Make fog render a infinite distance away. - // This doesn't technically disable Minecraft's fog - // so performance will probably be the same regardless, unlike - // Optifine's no fog setting. - - // we can't disable minecraft's fog outright because by default - // minecraft will re-enable the fog after our code - - RenderSystem.fogStart(0.0F); - RenderSystem.fogEnd(Float.MAX_VALUE); - RenderSystem.fogDensity(0.0F); - } - } - - - /** - * Translate the camera relative to the LodDimension's center, - * this is done since all LOD buffers are created in world space - * instead of object space. - * (since AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher - * accuracy vs the model view matrix, which only uses floats) - */ - private Matrix4f offsetTheModelViewMatrix(MatrixStack mcMatrixStack, float partialTicks) - { - // duplicate the last matrix - mcMatrixStack.pushPose(); - - - // get all relevant camera info - ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera(); - Vector3d projectedView = renderInfo.getPosition(); - - // translate the camera relative to the regions' center - // (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher - // accuracy vs the model view matrix, which only uses floats) - BlockPosWrapper bufferPos = vbosCenter.getWorldPosition(); - double xDiff = projectedView.x - bufferPos.getX(); - double zDiff = projectedView.z - bufferPos.getZ(); - mcMatrixStack.translate(-xDiff, -projectedView.y, -zDiff); - - - - // get the modified model view matrix - Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose(); - // remove the lod ModelViewMatrix - mcMatrixStack.popPose(); - - return lodModelViewMatrix; - } - - /** - * create a new projection matrix and send it over to the GPU - * @param currentProjectionMatrix this is Minecraft's current projection matrix - * @param vanillaBlockRenderedDistance Minecraft's vanilla far plane distance - * @param partialTicks how many ticks into the frame we are - */ - private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks) - { - Matrix4f lodPoj; - float nearClipPlane = LodConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get() ? vanillaBlockRenderedDistance / 5 : 1; - float farClipPlane = farPlaneBlockDistance * LodUtil.CHUNK_WIDTH >> 1; - - if (vivecraftDetected) - { - //use modify clip plane method to modify the current projection matrix's clip planes. - lodPoj = ReflectionHandler.INSTANCE.Matrix4fModifyClipPlanes( - currentProjectionMatrix, - nearClipPlane, - farClipPlane); - } - else - { - // create the new projection matrix - lodPoj = Matrix4f.perspective( - getFov(partialTicks, true), - (float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(), - nearClipPlane, - farClipPlane); - - // get Minecraft's un-edited projection matrix - // (this is before it is zoomed, distorted, etc.) - Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, true); - // true here means use "use fov setting" (probably) - - - // this logic strips away the defaultMcProj matrix, so we - // can get the distortionMatrix, which represents all - // transformations, zooming, distortions, etc. done - // to Minecraft's Projection matrix - Matrix4f defaultMcProjInv = defaultMcProj.copy(); - defaultMcProjInv.invert(); - - Matrix4f distortionMatrix = defaultMcProjInv.copy(); - distortionMatrix.multiply(currentProjectionMatrix); - - - // edit the lod projection to match Minecraft's - // (so the LODs line up with the real world) - lodPoj.multiply(distortionMatrix); - } - - // send the projection over to the GPU - gameRender.resetProjectionMatrix(lodPoj); - } - - - ///** setup the lighting to be used for the LODs */ - /*private void setupLighting(LodDimension lodDimension, float partialTicks) - { - // Determine if the player has night vision - boolean playerHasNightVision = false; - if (this.mc.getPlayer() != null) - { - Iterator iterator = this.mc.getPlayer().getActiveEffects().iterator(); - while (iterator.hasNext()) - { - EffectInstance instance = iterator.next(); - if (instance.getEffect() == Effects.NIGHT_VISION) - { - playerHasNightVision = true; - break; - } - } - } - - float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f; - sunBrightness = playerHasNightVision ? 1.0f : sunBrightness; - float gamma = (float) mc.getOptions().gamma - 0.0f; - float dayEffect = (sunBrightness - 0.2f) * 1.25f; - float lightStrength = (gamma * 0.34f - 0.01f) * (1.0f - dayEffect) + dayEffect - 0.20f; //gamma * 0.2980392157f + 0.1647058824f - float blueLightStrength = (gamma * 0.44f + 0.12f) * (1.0f - dayEffect) + dayEffect - 0.20f; //gamma * 0.4235294118f + 0.2784313725f - - float[] lightAmbient = {lightStrength, lightStrength, blueLightStrength, 1.0f}; - - - // can be used for debugging - // if (partialTicks < 0.005) - // ClientProxy.LOGGER.debug(lightStrength); - - ByteBuffer temp = ByteBuffer.allocateDirect(16); - temp.order(ByteOrder.nativeOrder()); - GL15.glLightfv(LOD_GL_LIGHT_NUMBER, GL15.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip()); - GL15.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting - - RenderSystem.enableLighting(); - }*/ - - /** Create all buffers that will be used. */ - public void setupBuffers(LodDimension lodDim) - { - lodBufferBuilder.setupBuffers(lodDim); - } - - - //======================// - // Other Misc Functions // - //======================// - - /** - * If this is called then the next time "drawLODs" is called - * the LODs will be regenerated; the same as if the player moved. - */ - public void regenerateLODsNextFrame() - { - fullRegen = true; - } - - - /** - * Replace the current Vertex Buffers with the newly - * created buffers from the lodBufferBuilder.

- *

- * For some reason this has to be called after the frame has been rendered, - * otherwise visual stuttering/rubber banding may happen. I'm not sure why... - */ - private void swapBuffers() - { - // replace the drawable buffers with - // the newly created buffers from the lodBufferBuilder - VertexBuffersAndOffset result = lodBufferBuilder.getVertexBuffers(); - vbos = result.vbos; - storageBufferIds = result.storageBufferIds; - vbosCenter = result.drawableCenterChunkPos; - } - - /** Calls the BufferBuilder's destroyBuffers method. */ - public void destroyBuffers() - { - lodBufferBuilder.destroyBuffers(); - } - - - private double getFov(float partialTicks, boolean useFovSetting) - { - return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting); - } - - - /** Return what fog settings should be used when rendering. */ - private NearFarFogSettings determineFogSettings() - { - NearFarFogSettings fogSettings = new NearFarFogSettings(); - - - FogQuality quality = ReflectionHandler.INSTANCE.getFogQuality(); - FogDrawOverride override = LodConfig.CLIENT.graphics.fogQualityOption.fogDrawOverride.get(); - - - fogSettings.vanillaIsRenderingFog = quality != FogQuality.OFF; - - - // use any fog overrides the user may have set - switch (override) - { - case FANCY: - quality = FogQuality.FANCY; - break; - - case NO_FOG: - quality = FogQuality.OFF; - break; - - case FAST: - quality = FogQuality.FAST; - break; - - case OPTIFINE_SETTING: - // don't override anything - break; - } - - - // only use fancy fog if the user's GPU can deliver - if (!GlProxy.getInstance().fancyFogAvailable && quality == FogQuality.FANCY) - { - quality = FogQuality.FAST; - } - - - // how different distances are drawn depends on the quality set - switch (quality) - { - case FANCY: - fogSettings.near.quality = FogQuality.FANCY; - fogSettings.far.quality = FogQuality.FANCY; - - switch (LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get()) - { - case NEAR_AND_FAR: - fogSettings.near.distance = FogDistance.NEAR; - fogSettings.far.distance = FogDistance.FAR; - break; - - case NEAR: - fogSettings.near.distance = FogDistance.NEAR; - fogSettings.far.distance = FogDistance.NEAR; - break; - - case FAR: - fogSettings.near.distance = FogDistance.FAR; - fogSettings.far.distance = FogDistance.FAR; - break; - } - break; - - case FAST: - fogSettings.near.quality = FogQuality.FAST; - fogSettings.far.quality = FogQuality.FAST; - - // fast fog setting should only have one type of - // fog, since the LODs are separated into a near - // and far portion; and fast fog is rendered from the - // frustrum's perspective instead of the camera - switch (LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get()) - { - case NEAR_AND_FAR: - case NEAR: - fogSettings.near.distance = FogDistance.NEAR; - fogSettings.far.distance = FogDistance.NEAR; - break; - - case FAR: - fogSettings.near.distance = FogDistance.FAR; - fogSettings.far.distance = FogDistance.FAR; - break; - } - break; - - case OFF: - fogSettings.near.quality = FogQuality.OFF; - fogSettings.far.quality = FogQuality.OFF; - break; - } - return fogSettings; - } - - - /** Determines if the LODs should have a fullRegen or partialRegen */ - private void determineIfLodsShouldRegenerate(LodDimension lodDim, float partialTicks) - { - - - short chunkRenderDistance = (short) mc.getRenderDistance(); - int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2; - - //=============// - // full regens // - //=============// - - // check if the view distance changed - if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() - || chunkRenderDistance != prevRenderDistance - || prevFogDistance != LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get()) - { - - vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; - DetailDistanceUtil.updateSettings(); - fullRegen = true; - previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk); - prevFogDistance = LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get(); - prevRenderDistance = chunkRenderDistance; - } - - // did the user change the debug setting? - if (LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get() != previousDebugMode) - { - previousDebugMode = LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get(); - fullRegen = true; - } - - - long newTime = System.currentTimeMillis(); - - // check if the player has moved - if (newTime - prevPlayerPosTime > LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get().playerMoveTimeout) - { - if (LevelPosUtil.getDetailLevel(previousPos) == 0 - || mc.getPlayerChunkPos().getX() != LevelPosUtil.getPosX(previousPos) - || mc.getPlayerChunkPos().getZ() != LevelPosUtil.getPosZ(previousPos)) - { - vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; - fullRegen = true; - previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayerChunkPos().getX(), mc.getPlayerChunkPos().getZ()); - } - prevPlayerPosTime = newTime; - } - - - - // determine how far the lighting has to - // change in order to rebuild the buffers - - // the max brightness is 1 and the minimum is 0.2 - float skyBrightness = lodDim.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f; - float minLightingDifference; - switch (LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get()) - { - case FREQUENT: - minLightingDifference = 0.025f; - break; - case NORMAL: - minLightingDifference = 0.05f; - break; - default: - case RARE: - minLightingDifference = 0.1f; - break; - } - - // check if the lighting changed - if (Math.abs(skyBrightness - prevSkyBrightness) > minLightingDifference - // make sure the lighting gets to the max/minimum value - // (just in case the minLightingDifference is too large to notice the change) - || (skyBrightness == 1.0f && prevSkyBrightness != 1.0f) // noon - || (skyBrightness == 0.2f && prevSkyBrightness != 0.2f) // midnight - || mc.getOptions().gamma != prevBrightness) - { - fullRegen = true; - prevBrightness = mc.getOptions().gamma; - prevSkyBrightness = skyBrightness; - } - - //================// - // partial regens // - //================// - - - // check if the vanilla rendered chunks changed - if (newTime - prevVanillaChunkTime > LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get().renderedChunkTimeout) - { - if (vanillaRenderedChunksChanged) - { - partialRegen = true; - vanillaRenderedChunksChanged = false; - } - prevVanillaChunkTime = newTime; - } - - - // check if there is any newly generated terrain to show - if (newTime - prevChunkTime > LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get().chunkChangeTimeout) - { - if (lodDim.regenDimensionBuffers) - { - partialRegen = true; - lodDim.regenDimensionBuffers = false; - } - prevChunkTime = newTime; - } - - - - //==============// - // LOD skipping // - //==============// - - // determine which LODs should not be rendered close to the player - HashSet chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayerBlockPos()); - int xIndex; - int zIndex; - for (ChunkPos pos : chunkPosToSkip) - { - vanillaRenderedChunksEmptySkip = false; - - xIndex = (pos.x - mc.getPlayerChunkPos().getX()) + (chunkRenderDistance + 1); - zIndex = (pos.z - mc.getPlayerChunkPos().getZ()) + (chunkRenderDistance + 1); - - // sometimes we are given chunks that are outside the render distance, - // This prevents index out of bounds exceptions - if (xIndex >= 0 && zIndex >= 0 - && xIndex < vanillaRenderedChunks.length - && zIndex < vanillaRenderedChunks.length) - { - if (!vanillaRenderedChunks[xIndex][zIndex]) - { - vanillaRenderedChunks[xIndex][zIndex] = true; - vanillaRenderedChunksChanged = true; - lodDim.markRegionBufferToRegen(pos.getRegionX(), pos.getRegionZ()); - } - } - } - - - // if the player is high enough, draw all LODs - if (chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256 && !vanillaRenderedChunksEmptySkip) - { - vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; - vanillaRenderedChunksChanged = true; - vanillaRenderedChunksEmptySkip = true; - } - } - -} diff --git a/src/main/java/com/seibel/lod/render/RenderUtil.java b/src/main/java/com/seibel/lod/render/RenderUtil.java deleted file mode 100644 index a0a1a83f5..000000000 --- a/src/main/java/com/seibel/lod/render/RenderUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.render; - -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; -import com.seibel.lod.wrappers.MinecraftWrapper; - -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.vector.Vector3d; - -/** - * This holds miscellaneous helper code - * to be used in the rendering process. - * - * @author James Seibel - * @version 10-19-2021 - */ -public class RenderUtil -{ - private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; - - - /** - * Returns if the given ChunkPos is in the loaded area of the world. - * @param center the center of the loaded world (probably the player's ChunkPos) - */ - public static boolean isChunkPosInLoadedArea(ChunkPosWrapper pos, ChunkPosWrapper center) - { - return (pos.getX() >= center.getX() - mc.getRenderDistance() - && pos.getX() <= center.getX() + mc.getRenderDistance()) - && - (pos.getZ() >= center.getZ() - mc.getRenderDistance() - && pos.getZ() <= center.getZ() + mc.getRenderDistance()); - } - - /** - * Returns if the given coordinate is in the loaded area of the world. - * @param centerCoordinate the center of the loaded world - */ - public static boolean isCoordinateInLoadedArea(int x, int z, int centerCoordinate) - { - return (x >= centerCoordinate - mc.getRenderDistance() - && x <= centerCoordinate + mc.getRenderDistance()) - && - (z >= centerCoordinate - mc.getRenderDistance() - && z <= centerCoordinate + mc.getRenderDistance()); - } - - - /** - * Find the coordinates that are in the center half of the given - * 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius). - */ - public static boolean isCoordinateInNearFogArea(int i, int j, int lodRadius) - { - int halfRadius = lodRadius / 2; - - return (i >= lodRadius - halfRadius - && i <= lodRadius + halfRadius) - && - (j >= lodRadius - halfRadius - && j <= lodRadius + halfRadius); - } - - - /** - * Returns true if one of the region's 4 corners is in front - * of the camera. - */ - public static boolean isRegionInViewFrustum(BlockPos playerBlockPos, Vector3d cameraDir, BlockPosWrapper vboCenterPos) - { - // convert the vbo position into a direction vector - // starting from the player's position - Vector3d vboVec = new Vector3d(vboCenterPos.getX(), 0, vboCenterPos.getZ()); - Vector3d playerVec = new Vector3d(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ()); - Vector3d vboCenterVec = vboVec.subtract(playerVec); - - - int halfRegionWidth = LodUtil.REGION_WIDTH / 2; - - // calculate the 4 corners - Vector3d vboSeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth); - Vector3d vboSwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth); - Vector3d vboNwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth); - Vector3d vboNeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth); - - // if any corner is visible, this region should be rendered - return isNormalizedVectorInViewFrustum(vboSeVec, cameraDir) || - isNormalizedVectorInViewFrustum(vboSwVec, cameraDir) || - isNormalizedVectorInViewFrustum(vboNwVec, cameraDir) || - isNormalizedVectorInViewFrustum(vboNeVec, cameraDir); - } - - /** - * Currently takes the dot product of the two vectors, - * but in the future could do more complicated frustum culling tests. - */ - private static boolean isNormalizedVectorInViewFrustum(Vector3d objectVector, Vector3d cameraDir) - { - // the -0.1 is to offer a slight buffer, so we are - // more likely to render LODs and thus, hopefully prevent - // flickering or odd disappearances - return objectVector.dot(cameraDir) > -0.1; - } -} diff --git a/src/main/java/com/seibel/lod/util/ColorUtil.java b/src/main/java/com/seibel/lod/util/ColorUtil.java deleted file mode 100644 index d8456b9aa..000000000 --- a/src/main/java/com/seibel/lod/util/ColorUtil.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.util; - -import java.awt.Color; - -import com.seibel.lod.wrappers.MinecraftWrapper; - -/** - * - * @author Cola - * @author Leonardo Amato - * @version ?? - */ -public class ColorUtil -{ - public static int rgbToInt(int red, int green, int blue) - { - return (0xFF << 24) | (red << 16) | (green << 8) | blue; - } - - public static int rgbToInt(int alpha, int red, int green, int blue) - { - return (alpha << 24) | (red << 16) | (green << 8) | blue; - } - - public static int getAlpha(int color) - { - return (color >> 24) & 0xFF; - } - - public static int getRed(int color) - { - return (color >> 16) & 0xFF; - } - - public static int getGreen(int color) - { - return (color >> 8) & 0xFF; - } - - public static int getBlue(int color) - { - return color & 0xFF; - } - - public static int applyShade(int color, int shade) - { - if (shade < 0) - return (getAlpha(color) << 24) | (Math.max(getRed(color) + shade, 0) << 16) | (Math.max(getGreen(color) + shade, 0) << 8) | Math.max(getBlue(color) + shade, 0); - else - return (getAlpha(color) << 24) | (Math.min(getRed(color) + shade, 255) << 16) | (Math.min(getGreen(color) + shade, 255) << 8) | Math.min(getBlue(color) + shade, 255); - } - - public static int applyShade(int color, float shade) - { - if (shade < 1) - return (getAlpha(color) << 24) | ((int) Math.max(getRed(color) * shade, 0) << 16) | ((int) Math.max(getGreen(color) * shade, 0) << 8) | (int) Math.max(getBlue(color) * shade, 0); - else - return (getAlpha(color) << 24) | ((int) Math.min(getRed(color) * shade, 255) << 16) | ((int) Math.min(getGreen(color) * shade, 255) << 8) | (int) Math.min(getBlue(color) * shade, 255); - } - - /** This method apply the lightmap to the color to use */ - public static int applyLightValue(int color, int skyLight, int blockLight) - { - int lightColor = MinecraftWrapper.INSTANCE.getColorIntFromLightMap(blockLight, skyLight); - int red = ColorUtil.getBlue(lightColor); - int green = ColorUtil.getGreen(lightColor); - int blue = ColorUtil.getRed(lightColor); - - return ColorUtil.multiplyRGBcolors(color, ColorUtil.rgbToInt(red, green, blue)); - } - - /** Edit the given color as an HSV (Hue Saturation Value) color */ - public static int applySaturationAndBrightnessMultipliers(int color, float saturationMultiplier, float brightnessMultiplier) - { - float[] hsv = Color.RGBtoHSB(getRed(color), getGreen(color), getBlue(color), null); - return Color.getHSBColor( - hsv[0], // hue - LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f), - LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f)).getRGB(); - } - - /** Multiply 2 RGB colors */ - public static int multiplyRGBcolors(int color1, int color2) - { - return ((getAlpha(color1) * getAlpha(color2) / 255) << 24) | ((getRed(color1) * getRed(color2) / 255) << 16) | ((getGreen(color1) * getGreen(color2) / 255) << 8) | (getBlue(color1) * getBlue(color2) / 255); - } - - @SuppressWarnings("unused") - public static String toString(int color) - { - return Integer.toHexString(getAlpha(color)) + " " + - Integer.toHexString(getRed(color)) + " " + - Integer.toHexString(getGreen(color)) + " " + - Integer.toHexString(getBlue(color)); - } -} diff --git a/src/main/java/com/seibel/lod/util/DataPointUtil.java b/src/main/java/com/seibel/lod/util/DataPointUtil.java deleted file mode 100644 index 5bc568e90..000000000 --- a/src/main/java/com/seibel/lod/util/DataPointUtil.java +++ /dev/null @@ -1,520 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.util; - -import static com.seibel.lod.builders.bufferBuilding.LodBufferBuilder.skyLightPlayer; - -import com.seibel.lod.enums.DistanceGenerationMode; - -/** - * - * @author Leonardo Amato - * @version ?? - */ -public class DataPointUtil -{ - /* - |a |a |a |a |r |r |r |r | - - |r |r |r |r |g |g |g |g | - - |g |g |g |g |b |b |b |b | - - |b |b |b |b |h |h |h |h | - - |h |h |h |h |h |h |d |d | - - |d |d |d |d |d |d |d |d | - - |bl |bl |bl |bl |sl |sl |sl |sl | - - |l |l |f |g |g |g |v |e | - - - */ - - // Reminder: bytes have range of [-128, 127]. - // When converting to or from an int a 128 should be added or removed. - // If there is a bug with color then it's probably caused by this. - - //To be used in the future for negative value - //public final static int MIN_DEPTH = -64; - //public final static int MIN_HEIGHT = -64; - public final static int EMPTY_DATA = 0; - public static int worldHeight = 256; - - public final static int ALPHA_DOWNSIZE_SHIFT = 4; - - //public final static int BLUE_COLOR_SHIFT = 0; - //public final static int GREEN_COLOR_SHIFT = 8; - //public final static int RED_COLOR_SHIFT = 16; - //public final static int ALPHA_COLOR_SHIFT = 24; - - public final static int BLUE_SHIFT = 36; - public final static int GREEN_SHIFT = BLUE_SHIFT + 8; - public final static int RED_SHIFT = BLUE_SHIFT + 16; - public final static int ALPHA_SHIFT = BLUE_SHIFT + 24; - - public final static int COLOR_SHIFT = 36; - - public final static int HEIGHT_SHIFT = 26; - public final static int DEPTH_SHIFT = 16; - public final static int BLOCK_LIGHT_SHIFT = 12; - public final static int SKY_LIGHT_SHIFT = 8; - //public final static int LIGHTS_SHIFT = SKY_LIGHT_SHIFT; - //public final static int VERTICAL_INDEX_SHIFT = 6; - public final static int FLAG_SHIFT = 5; - public final static int GEN_TYPE_SHIFT = 2; - public final static int VOID_SHIFT = 1; - public final static int EXISTENCE_SHIFT = 0; - - public final static long ALPHA_MASK = 0b1111; - public final static long RED_MASK = 0b1111_1111; - public final static long GREEN_MASK = 0b1111_1111; - public final static long BLUE_MASK = 0b1111_1111; - public final static long COLOR_MASK = 0b11111111_11111111_11111111; - public final static long HEIGHT_MASK = 0b11_1111_1111; - public final static long DEPTH_MASK = 0b11_1111_1111; - //public final static long LIGHTS_MASK = 0b1111_1111; - public final static long BLOCK_LIGHT_MASK = 0b1111; - public final static long SKY_LIGHT_MASK = 0b1111; - //public final static long VERTICAL_INDEX_MASK = 0b11; - public final static long FLAG_MASK = 0b1; - public final static long GEN_TYPE_MASK = 0b111; - public final static long VOID_MASK = 1; - public final static long EXISTENCE_MASK = 1; - - - public static long createVoidDataPoint(int generationMode) - { - long dataPoint = 0; - dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT; - dataPoint += VOID_MASK << VOID_SHIFT; - dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT; - return dataPoint; - } - - public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode, boolean flag) - { - return createDataPoint( - ColorUtil.getAlpha(color), - ColorUtil.getRed(color), - ColorUtil.getGreen(color), - ColorUtil.getBlue(color), - height, depth, lightSky, lightBlock, generationMode, flag); - } - - public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode, boolean flag) - { - long dataPoint = 0; - dataPoint += (long) (alpha >>> ALPHA_DOWNSIZE_SHIFT) << ALPHA_SHIFT; - dataPoint += (red & RED_MASK) << RED_SHIFT; - dataPoint += (green & GREEN_MASK) << GREEN_SHIFT; - dataPoint += (blue & BLUE_MASK) << BLUE_SHIFT; - dataPoint += (height & HEIGHT_MASK) << HEIGHT_SHIFT; - dataPoint += (depth & DEPTH_MASK) << DEPTH_SHIFT; - dataPoint += (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT; - dataPoint += (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT; - dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT; - if (flag) dataPoint += FLAG_MASK << FLAG_SHIFT; - dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT; - - return dataPoint; - } - - public static short getHeight(long dataPoint) - { - return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK); - } - - public static short getDepth(long dataPoint) - { - return (short) ((dataPoint >>> DEPTH_SHIFT) & DEPTH_MASK); - } - - public static short getAlpha(long dataPoint) - { - return (short) ((((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT) | 0b1111); - } - - public static short getRed(long dataPoint) - { - return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK); - } - - public static short getGreen(long dataPoint) - { - return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK); - } - - public static short getBlue(long dataPoint) - { - return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK); - } - - public static byte getLightSky(long dataPoint) - { - return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK); - } - - public static byte getLightSkyAlt(long dataPoint) - { - if (skyLightPlayer == 0 && ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1) - return 0; - else - return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK); - } - - public static byte getLightBlock(long dataPoint) - { - return (byte) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK); - } - - public static boolean getFlag(long dataPoint) - { - return ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1; - } - - public static byte getGenerationMode(long dataPoint) - { - return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK); - } - - - public static boolean isVoid(long dataPoint) - { - return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1); - } - - public static boolean doesItExist(long dataPoint) - { - return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1); - } - - public static int getColor(long dataPoint) - { - return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (/*((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111)*/255 << 24)); - } - - /** This is used to convert a dataPoint to string (useful for the print function) */ - @SuppressWarnings("unused") - public static String toString(long dataPoint) - { - return getHeight(dataPoint) + " " + - getDepth(dataPoint) + " " + - getAlpha(dataPoint) + " " + - getRed(dataPoint) + " " + - getBlue(dataPoint) + " " + - getGreen(dataPoint) + " " + - getLightBlock(dataPoint) + " " + - getLightSky(dataPoint) + " " + - getGenerationMode(dataPoint) + " " + - isVoid(dataPoint) + " " + - doesItExist(dataPoint) + '\n'; - } - - public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize) - { - start *= packetSize; - length *= packetSize; - arraySize *= packetSize; - for (int i = 0; i < arraySize - start; i++) - { - array[start + i] = array[start + length + i]; - //remove comment to not leave garbage at the end - //array[start + packetSize + i] = 0; - } - } - - public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize) - { - start *= packetSize; - length *= packetSize; - arraySize *= packetSize; - for (int i = arraySize - start - 1; i >= 0; i--) - { - array[start + length + i] = array[start + i]; - array[start + i] = 0; - } - } - - /** - * This method merge column of multiple data together - * @param dataToMerge one or more columns of data - * @param inputVerticalData vertical size of an input data - * @param maxVerticalData max vertical size of the merged data - * @return one column of correctly parsed data - */ - public static long[] mergeMultiData(long[] dataToMerge, int inputVerticalData, int maxVerticalData) - { - int size = dataToMerge.length / inputVerticalData; - - // We initialize the arrays that are going to be used - short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((worldHeight / 2 + 1) * 2); - long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0)); - - - int genMode = DistanceGenerationMode.SERVER.complexity; - boolean allEmpty = true; - boolean allVoid = true; - boolean allDefault; - long singleData; - - - short depth; - short height; - int count = 0; - int i; - int ii; - int dataIndex; - //We collect the indexes of the data, ordered by the depth - for (int index = 0; index < size; index++) - { - for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++) - { - singleData = dataToMerge[index * inputVerticalData + dataIndex]; - if (doesItExist(singleData)) - { - genMode = Math.min(genMode, getGenerationMode(singleData)); - allEmpty = false; - if (!isVoid(singleData)) - { - allVoid = false; - depth = getDepth(singleData); - height = getHeight(singleData); - - int botPos = -1; - int topPos = -1; - //values fall in between and possibly require extension of array - boolean botExtend = false; - boolean topExtend = false; - for (i = 0; i < count; i++) - { - if (depth <= heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1]) - { - botPos = i; - break; - } - else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth > heightAndDepth[(i + 1) * 2]) || i + 1 == count)) - { - botPos = i; - botExtend = true; - break; - } - } - for (i = 0; i < count; i++) - { - if (height <= heightAndDepth[i * 2] && height >= heightAndDepth[i * 2 + 1]) - { - topPos = i; - break; - } - else if (height < heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count)) - { - topPos = i; - topExtend = true; - break; - } - } - if (topPos == -1) - { - if (botPos == -1) - { - //whole block falls above - extendArray(heightAndDepth, 2, 0, 1, count); - heightAndDepth[0] = height; - heightAndDepth[1] = depth; - count++; - } - else if (!botExtend) - { - //only top falls above extending it there, while bottom is inside existing - shrinkArray(heightAndDepth, 2, 0, botPos, count); - heightAndDepth[0] = height; - count -= botPos; - } - else - { - //top falls between some blocks, extending those as well - shrinkArray(heightAndDepth, 2, 0, botPos, count); - heightAndDepth[0] = height; - heightAndDepth[1] = depth; - count -= botPos; - } - } - else if (!topExtend) - { - if (!botExtend) - //both top and bottom are within some exiting blocks, possibly merging them - heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1]; - else - //top falls between some blocks, extending it there - heightAndDepth[topPos * 2 + 1] = depth; - shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); - count -= botPos - topPos; - } - else - { - if (!botExtend) - { - //only top is within some exiting block, extending it - topPos++; //to make it easier - heightAndDepth[topPos * 2] = height; - heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1]; - shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); - count -= botPos - topPos; - } - else - { - //both top and bottom are outside existing blocks - shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); - count -= botPos - topPos; - extendArray(heightAndDepth, 2, topPos + 1, 1, count); - count++; - heightAndDepth[topPos * 2 + 2] = height; - heightAndDepth[topPos * 2 + 3] = depth; - } - } - } - } - else - break; - } - } - - //We check if there is any data that's not empty or void - if (allEmpty) - return dataPoint; - if (allVoid) - { - dataPoint[0] = createVoidDataPoint(genMode); - return dataPoint; - } - - //we limit the vertical portion to maxVerticalData - int j = 0; - while (count > maxVerticalData) - { - ii = worldHeight; - for (i = 0; i < count - 1; i++) - { - if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii) - { - ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2]; - j = i; - } - } - heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1]; - for (i = j + 1; i < count - 1; i++) - { - heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2]; - heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1]; - } - //System.arraycopy(heightAndDepth, j + 1, heightAndDepth, j, count - j - 1); - count--; - } - //As standard the vertical lods are ordered from top to bottom - for (j = count - 1; j >= 0; j--) - { - height = heightAndDepth[j * 2]; - depth = heightAndDepth[j * 2 + 1]; - - if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2) - break; - - int numberOfChildren = 0; - int tempAlpha = 0; - int tempRed = 0; - int tempGreen = 0; - int tempBlue = 0; - int tempLightBlock = 0; - int tempLightSky = 0; - byte tempGenMode = DistanceGenerationMode.SERVER.complexity; - allEmpty = true; - allVoid = true; - allDefault = true; - long data = 0; - - for (int index = 0; index < size; index++) - { - for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++) - { - singleData = dataToMerge[index * inputVerticalData + dataIndex]; - if (doesItExist(singleData) && !isVoid(singleData)) - { - - if ((depth <= getDepth(singleData) && getDepth(singleData) <= height) - || (depth <= getHeight(singleData) && getHeight(singleData) <= height)) - { - if (getHeight(singleData) > getHeight(data)) - data = singleData; - } - } - else - break; - } - if (!doesItExist(data)) - { - singleData = dataToMerge[index * inputVerticalData]; - data = createVoidDataPoint(getGenerationMode(singleData)); - } - - if (doesItExist(data)) - { - allEmpty = false; - if (!isVoid(data)) - { - numberOfChildren++; - allVoid = false; - tempAlpha += getAlpha(data); - tempRed += getRed(data); - tempGreen += getGreen(data); - tempBlue += getBlue(data); - tempLightBlock += getLightBlock(data); - tempLightSky += getLightSky(data); - if (!getFlag(data)) allDefault = false; - } - tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data)); - } - else - tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity); - } - - if (allEmpty) - //no child has been initialized - dataPoint[j] = EMPTY_DATA; - else if (allVoid) - //all the children are void - dataPoint[j] = createVoidDataPoint(tempGenMode); - else - { - //we have at least 1 child - tempAlpha = tempAlpha / numberOfChildren; - tempRed = tempRed / numberOfChildren; - tempGreen = tempGreen / numberOfChildren; - tempBlue = tempBlue / numberOfChildren; - tempLightBlock = tempLightBlock / numberOfChildren; - tempLightSky = tempLightSky / numberOfChildren; - dataPoint[j] = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault); - } - } - return dataPoint; - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java deleted file mode 100644 index 61f38942e..000000000 --- a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.util; - -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.enums.HorizontalQuality; -import com.seibel.lod.enums.HorizontalResolution; -import com.seibel.lod.wrappers.MinecraftWrapper; - -/** - * - * @author Leonardo Amato - * @version ?? - */ -public class DetailDistanceUtil -{ - private static final double genMultiplier = 1.0; - private static final double treeGenMultiplier = 1.0; - private static final double treeCutMultiplier = 1.0; - private static byte minGenDetail = LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel; - private static byte minDrawDetail = (byte) Math.max(LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel, LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel); - private static final int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1; - private static final int minDistance = 0; - private static int minDetailDistance = (int) (MinecraftWrapper.INSTANCE.getRenderDistance()*16 * 1.42f); - private static int maxDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * 16 * 2; - - - private static final HorizontalResolution[] lodGenDetails = { - HorizontalResolution.BLOCK, - HorizontalResolution.TWO_BLOCKS, - HorizontalResolution.FOUR_BLOCKS, - HorizontalResolution.HALF_CHUNK, - HorizontalResolution.CHUNK, - HorizontalResolution.CHUNK, - HorizontalResolution.CHUNK, - HorizontalResolution.CHUNK, - HorizontalResolution.CHUNK, - HorizontalResolution.CHUNK, - HorizontalResolution.CHUNK }; - - - - public static void updateSettings() - { - minDetailDistance = (int) (MinecraftWrapper.INSTANCE.getRenderDistance()*16 * 1.42f); - minGenDetail = LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel; - minDrawDetail = (byte) Math.max(LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel, LodConfig.CLIENT.graphics.qualityOption.drawResolution.get().detailLevel); - maxDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * 16 * 8; - } - - public static int baseDistanceFunction(int detail) - { - if (detail <= minGenDetail) - return minDistance; - if (detail >= maxDetail) - return maxDistance; - - if (LodConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.get()) - return detail * 0x10000; //if you want more you are doing wrong - - int distanceUnit = LodConfig.CLIENT.graphics.qualityOption.horizontalScale.get().distanceUnit; - if (LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get() == HorizontalQuality.LOWEST) - return (detail * distanceUnit); - else - { - double base = LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get().quadraticBase; - return (int) (Math.pow(base, detail) * distanceUnit); - } - } - - public static int getDrawDistanceFromDetail(int detail) - { - return baseDistanceFunction(detail); - } - - public static byte baseInverseFunction(int distance, byte minDetail, boolean useRenderMinDistance) - { - int detail; - if (distance == 0 - || (distance < minDetailDistance && useRenderMinDistance) - || LodConfig.CLIENT.graphics.advancedGraphicsOption.alwaysDrawAtMaxQuality.get()) - return minDetail; - int distanceUnit = LodConfig.CLIENT.graphics.qualityOption.horizontalScale.get().distanceUnit; - if (LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get() == HorizontalQuality.LOWEST) - detail = (byte) distance / distanceUnit; - else - { - double base = LodConfig.CLIENT.graphics.qualityOption.horizontalQuality.get().quadraticBase; - double logBase = Math.log(base); - //noinspection IntegerDivisionInFloatingPointContext - detail = (byte) (Math.log(distance / distanceUnit) / logBase); - } - return (byte) LodUtil.clamp(minDetail, detail, maxDetail - 1); - } - - public static byte getDrawDetailFromDistance(int distance) - { - return baseInverseFunction(distance, minDrawDetail, false); - } - - public static byte getGenerationDetailFromDistance(int distance) - { - return baseInverseFunction((int) (distance * genMultiplier), minGenDetail, true); - } - - public static byte getTreeCutDetailFromDistance(int distance) - { - return baseInverseFunction((int) (distance * treeCutMultiplier), minGenDetail, true); - } - - public static byte getTreeGenDetailFromDistance(int distance) - { - return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail, true); - } - - public static DistanceGenerationMode getDistanceGenerationMode(int detail) - { - return LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get(); - } - - public static byte getLodDrawDetail(int detail) - { - if (detail < minDrawDetail) - return (byte) minDrawDetail; - else - return (byte) detail; - } - - public static HorizontalResolution getLodGenDetail(int detail) - { - if (detail < minGenDetail) - return lodGenDetails[minGenDetail]; - else - return lodGenDetails[detail]; - } - - - public static byte getCutLodDetail(int detail) - { - if (detail < minGenDetail) - return lodGenDetails[minGenDetail].detailLevel; - else if (detail == maxDetail) - return LodUtil.REGION_DETAIL_LEVEL; - else - return lodGenDetails[detail].detailLevel; - } - - public static int getMaxVerticalData(int detail) - { - return LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get().maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)]; - } - -} diff --git a/src/main/java/com/seibel/lod/util/LevelPosUtil.java b/src/main/java/com/seibel/lod/util/LevelPosUtil.java deleted file mode 100644 index 11cbfc4f4..000000000 --- a/src/main/java/com/seibel/lod/util/LevelPosUtil.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.util; - -/** - * - * @author Leonardo Amato - * @version ?? - */ -public class LevelPosUtil -{ - public static int[] convert(int[] levelPos, byte newDetailLevel) - { - return convert(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos), newDetailLevel); - } - - public static int[] convert(byte detailLevel, int posX, int posZ, byte newDetailLevel) - { - int width; - if (newDetailLevel >= detailLevel) - { - width = 1 << (newDetailLevel - detailLevel); - return createLevelPos( - newDetailLevel, - Math.floorDiv(posX, width), - Math.floorDiv(posZ, width)); - } - else - { - width = 1 << (detailLevel - newDetailLevel); - return createLevelPos( - newDetailLevel, - posX * width, - posZ * width); - } - } - - public static int[] createLevelPos(byte detailLevel, int posX, int posZ) - { - return new int[] { detailLevel, posX, posZ }; - } - - public static int convert(byte detailLevel, int pos, byte newDetailLevel) - { - int width; - if (newDetailLevel >= detailLevel) - { - width = 1 << (newDetailLevel - detailLevel); - return Math.floorDiv(pos, width); - } - else - { - width = 1 << (detailLevel - newDetailLevel); - return pos * width; - } - } - - public static int getRegion(byte detailLevel, int pos) - { - return Math.floorDiv(pos, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel)); - } - - public static int getRegionModule(byte detailLevel, int pos) - { - return Math.floorMod(pos, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel)); - } - - public static byte getDetailLevel(int[] levelPos) - { - return (byte) levelPos[0]; - } - - public static int getPosX(int[] levelPos) - { - return levelPos[1]; - } - - public static int getPosZ(int[] levelPos) - { - return levelPos[2]; - } - - public static int getDistance(int[] levelPos) - { - return levelPos[3]; - } - - public static int[] getRegionModule(int[] levelPos) - { - return getRegionModule(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos)); - } - - public static int[] getRegionModule(byte detailLevel, int posX, int posZ) - { - int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - return createLevelPos( - detailLevel, - Math.floorMod(posX, width), - Math.floorMod(posZ, width)); - } - - public static int[] applyOffset(int[] levelPos, int xOffset, int zOffset) - { - return createLevelPos( - getDetailLevel(levelPos), - getPosX(levelPos) + xOffset, - getPosZ(levelPos) + zOffset); - } - - public static int[] applyLevelOffset(int[] levelPos, byte detailOffset, int xOffset, int zOffset) - { - return createLevelPos( - getDetailLevel(levelPos), - getPosX(levelPos) + xOffset * (1 << detailOffset), - getPosZ(levelPos) + zOffset * (1 << detailOffset)); - } - - public static int getRegionPosX(int[] levelPos) - { - int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - getDetailLevel(levelPos)); - return Math.floorDiv(getPosX(levelPos), width); - } - - public static int getRegionPosZ(int[] levelPos) - { - int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - getDetailLevel(levelPos)); - return Math.floorDiv(getPosZ(levelPos), width); - } - - public static int getChunkPos(byte detailLevel, int pos) - { - return convert(detailLevel, pos, LodUtil.CHUNK_DETAIL_LEVEL); - } - - public static int myPow2(int x) - { - return x*x; - } - - public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ) - { - int width = 1 << detailLevel; - - int startPosX = posX * width; - int startPosZ = posZ * width; - int endPosX = myPow2(playerPosX - startPosX - width); - int endPosZ = myPow2(playerPosZ - startPosZ - width); - startPosX = myPow2(playerPosX - startPosX); - startPosZ = myPow2(playerPosZ - startPosZ); - - int maxDistance = (int) Math.sqrt(startPosX + startPosZ); - maxDistance = Math.max(maxDistance, (int) Math.sqrt(startPosX + endPosZ)); - maxDistance = Math.max(maxDistance, (int) Math.sqrt(endPosX + startPosZ)); - maxDistance = Math.max(maxDistance, (int) Math.sqrt(endPosX + endPosZ)); - - return maxDistance; - } - - public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion) - { - int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - int newPosX = xRegion * width + posX; - int newPosZ = zRegion * width + posZ; - return maxDistance(detailLevel, newPosX, newPosZ, playerPosX, playerPosZ); - } - - - public static int minDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ) - { - int width = 1 << detailLevel; - - int startPosX = posX * width; - int startPosZ = posZ * width; - int endPosX = startPosX + width; - int endPosZ = startPosZ + width; - - boolean inXArea = playerPosX >= startPosX && playerPosX <= endPosX; - boolean inZArea = playerPosZ >= startPosZ && playerPosZ <= endPosZ; - if (inXArea && inZArea) - return 0; - else if (inXArea) - { - return Math.min( - Math.abs(playerPosZ - startPosZ), - Math.abs(playerPosZ - endPosZ) - ); - } - else if (inZArea) - { - return Math.min( - Math.abs(playerPosX - startPosX), - Math.abs(playerPosX - endPosX) - ); - } - else - { - startPosX = myPow2(playerPosX - startPosX); - startPosZ = myPow2(playerPosZ - startPosZ); - endPosX = myPow2(playerPosX - endPosX); - endPosZ = myPow2(playerPosZ - endPosZ); - - int minDistance = (int) Math.sqrt(startPosX + startPosZ); - minDistance = Math.min(minDistance, (int) Math.sqrt(startPosX + endPosZ)); - minDistance = Math.min(minDistance, (int) Math.sqrt(endPosX + startPosZ)); - minDistance = Math.min(minDistance, (int) Math.sqrt(endPosX + endPosZ)); - return minDistance; - } - } - - public static int minDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion) - { - int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - int newPosX = xRegion * width + posX; - int newPosZ = zRegion * width + posZ; - return minDistance(detailLevel, newPosX, newPosZ, playerPosX, playerPosZ); - } - - public static int compareDistance(int firstDistance, int secondDistance) - { - return Integer.compare( - firstDistance, - secondDistance); - } - - - public static int compareLevelAndDistance(byte firstDetail, int firstDistance, byte secondDetail, int secondDistance) - { - int compareResult = Integer.compare( - secondDetail, - firstDetail); - if (compareResult == 0) - { - compareResult = Integer.compare( - firstDistance, - secondDistance); - } - return compareResult; - } - - @SuppressWarnings("unused") - public static String toString(int[] levelPos) - { - return (getDetailLevel(levelPos) + " " - + getPosX(levelPos) + " " - + getPosZ(levelPos)); - } - - public static String toString(byte detailLevel, int posX, int posZ) - { - return (detailLevel + " " + posX + " " + posZ); - } -} diff --git a/src/main/java/com/seibel/lod/util/LodUtil.java b/src/main/java/com/seibel/lod/util/LodUtil.java deleted file mode 100644 index ae3bd4f79..000000000 --- a/src/main/java/com/seibel/lod/util/LodUtil.java +++ /dev/null @@ -1,501 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.util; - -import java.awt.Color; -import java.io.File; -import java.util.HashSet; - -import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box; -import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.HorizontalResolution; -import com.seibel.lod.enums.VanillaOverdraw; -import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.objects.RegionPos; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; -import com.seibel.lod.wrappers.MinecraftWrapper; - -import net.minecraft.client.multiplayer.ServerData; -import net.minecraft.client.renderer.WorldRenderer; -import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.renderer.vertex.VertexFormat; -import net.minecraft.server.integrated.IntegratedServer; -import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.DimensionType; -import net.minecraft.world.IWorld; -import net.minecraft.world.chunk.ChunkSection; -import net.minecraft.world.chunk.IChunk; -import net.minecraft.world.gen.Heightmap; -import net.minecraft.world.server.ServerChunkProvider; -import net.minecraft.world.server.ServerWorld; - -/** - * This class holds methods and constants that may be used in multiple places. - * - * @author James Seibel - * @version 10-20-2021 - */ -public class LodUtil -{ - private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; - - /** - * Vanilla render distances less than or equal to this will not allow partial - * overdraw. The VanillaOverdraw will either be ALWAYS or NEVER. - */ - public static final int MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW = 4; - - /** - * Vanilla render distances less than or equal to this will cause the overdraw to - * run at a smaller fraction of the vanilla render distance. - */ - public static final int MINIMUM_RENDER_DISTANCE_FOR_FAR_OVERDRAW = 11; - - - - - /** The maximum number of LODs that can be rendered vertically */ - public static final int MAX_NUMBER_OF_VERTICAL_LODS = 32; - - /** - * alpha used when drawing chunks in debug mode - */ - public static final int DEBUG_ALPHA = 255; // 0 - 255 - public static final Color COLOR_DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA); - public static final Color COLOR_DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA); - public static final Color COLOR_INVISIBLE = new Color(0, 0, 0, 0); - - public static final int CEILED_DIMENSION_MAX_RENDER_DISTANCE = 64; // 0 - 255 - - /** - * In order of nearest to farthest:
- * Red, Orange, Yellow, Green, Cyan, Blue, Magenta, white, gray, black - */ - public static final Color[] DEBUG_DETAIL_LEVEL_COLORS = new Color[] { Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.WHITE, Color.GRAY, Color.BLACK }; - - - public static final byte DETAIL_OPTIONS = 10; - - /** 512 blocks wide */ - public static final byte REGION_DETAIL_LEVEL = DETAIL_OPTIONS - 1; - /** 16 blocks wide */ - public static final byte CHUNK_DETAIL_LEVEL = 4; - /** 1 block wide */ - public static final byte BLOCK_DETAIL_LEVEL = 0; - - public static final short MAX_VERTICAL_DATA = 4; - - /** - * measured in Blocks
- * detail level max - 1 - */ - public static final short REGION_WIDTH = 1 << REGION_DETAIL_LEVEL; - /** - * measured in Blocks
- * detail level 4 - */ - public static final short CHUNK_WIDTH = 16; - /** - * measured in Blocks
- * detail level 0 - */ - public static final short BLOCK_WIDTH = 1; - - - /** number of chunks wide */ - public static final int REGION_WIDTH_IN_CHUNKS = REGION_WIDTH / CHUNK_WIDTH; - - - /** - * If we ever need to use a heightmap for any reason, use this one. - */ - public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; - - /** - * This regex finds any characters that are invalid for use in a windows - * (and by extension mac and linux) file path - */ - public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]"; - - /** - * 64 MB by default is the maximum amount of memory that - * can be directly allocated.

- *

- * I know there are commands to change that amount - * (specifically "-XX:MaxDirectMemorySize"), but - * I have no idea how to access that amount.
- * So I guess this will be the hard limit for now.

- *

- * https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx - */ - public static final int MAX_ALLOCATABLE_DIRECT_MEMORY = 64 * 1024 * 1024; - - - public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR; - - - - - - /** - * Gets the first valid ServerWorld. - * @return null if there are no ServerWorlds - */ - public static ServerWorld getFirstValidServerWorld() - { - if (mc.hasSinglePlayerServer()) - return null; - - Iterable worlds = mc.getSinglePlayerServer().getAllLevels(); - - for (ServerWorld world : worlds) - return world; - - return null; - } - - /** - * Gets the ServerWorld for the relevant dimension. - * @return null if there is no ServerWorld for the given dimension - */ - public static ServerWorld getServerWorldFromDimension(DimensionType dimension) - { - IntegratedServer server = mc.getSinglePlayerServer(); - if (server == null) - return null; - - Iterable worlds = server.getAllLevels(); - ServerWorld returnWorld = null; - - for (ServerWorld world : worlds) - { - if (world.dimensionType() == dimension) - { - returnWorld = world; - break; - } - } - - return returnWorld; - } - - /** Convert a 2D absolute position into a quad tree relative position. */ - public static RegionPos convertGenericPosToRegionPos(int x, int z, int detailLevel) - { - int relativePosX = Math.floorDiv(x, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel)); - int relativePosZ = Math.floorDiv(z, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel)); - - return new RegionPos(relativePosX, relativePosZ); - } - - /** Convert a 2D absolute position into a quad tree relative position. */ - public static int convertLevelPos(int pos, int currentDetailLevel, int targetDetailLevel) - { - return pos / (1 << (targetDetailLevel - currentDetailLevel)); - } - - /** - * Return whether the given chunk - * has any data in it. - */ - public static boolean chunkHasBlockData(IChunk chunk) - { - ChunkSection[] blockStorage = chunk.getSections(); - - for (ChunkSection section : blockStorage) - { - if (section != null && !section.isEmpty()) - return true; - } - - return false; - } - - - /** - * If on single player this will return the name of the user's - * world, if in multiplayer it will return the server name, IP, - * and game version. - */ - public static String getWorldID(IWorld world) - { - if (mc.hasSinglePlayerServer()) - { - // chop off the dimension ID as it is not needed/wanted - String dimId = getDimensionIDFromWorld(world); - - // get the world name - int saveIndex = dimId.indexOf("saves") + 1 + "saves".length(); - int slashIndex = dimId.indexOf(File.separatorChar, saveIndex); - dimId = dimId.substring(saveIndex, slashIndex); - return dimId; - } - else - { - return getServerId(); - } - } - - - /** - * If on single player this will return the name of the user's - * world and the dimensional save folder, if in multiplayer - * it will return the server name, ip, game version, and dimension.
- *
- * This can be used to determine where to save files for a given - * dimension. - */ - public static String getDimensionIDFromWorld(IWorld world) - { - if (mc.hasSinglePlayerServer()) - { - // this will return the world save location - // and the dimension folder - - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(world.dimensionType()); - if (serverWorld == null) - throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerWorld for the dimension " + world.dimensionType().effectsLocation().getPath()); - - ServerChunkProvider provider = serverWorld.getChunkSource(); - if (provider == null) - throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the ServerChunkProvider for the dimension " + world.dimensionType().effectsLocation().getPath()); - - return provider.dataStorage.dataFolder.toString(); - } - else - { - return getServerId() + File.separatorChar + "dim_" + world.dimensionType().effectsLocation().getPath() + File.separatorChar; - } - } - - /** returns the server name, IP and game version. */ - public static String getServerId() - { - ServerData server = mc.getCurrentServer(); - String serverName = server.name.replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); - String serverIp = server.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); - String serverMcVersion = server.version.getString().replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); - - return serverName + ", IP " + serverIp + ", GameVersion " + serverMcVersion; - } - - - /** Convert a BlockColors int into a Color object */ - public static Color intToColor(int num) - { - int filter = 0b11111111; - - int red = (num >> 16) & filter; - int green = (num >> 8) & filter; - int blue = num & filter; - - return new Color(red, green, blue); - } - - /** Convert a Color into a BlockColors object. */ - public static int colorToInt(Color color) - { - return color.getRGB(); - } - - - /** - * Clamps the given value between the min and max values. - * May behave strangely if min > max. - */ - public static int clamp(int min, int value, int max) - { - return Math.min(max, Math.max(value, min)); - } - - /** - * Clamps the given value between the min and max values. - * May behave strangely if min > max. - */ - public static float clamp(float min, float value, float max) - { - return Math.min(max, Math.max(value, min)); - } - - /** - * Clamps the given value between the min and max values. - * May behave strangely if min > max. - */ - public static double clamp(double min, double value, double max) - { - return Math.min(max, Math.max(value, min)); - } - - /** - * Get a HashSet of all ChunkPos within the normal render distance - * that should not be rendered. - */ - public static HashSet getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPosWrapper blockPosWrapper) - { - int chunkRenderDist = mc.getRenderDistance(); - ChunkPosWrapper centerChunk = new ChunkPosWrapper(blockPosWrapper); - - int skipRadius; - VanillaOverdraw overdraw = LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get(); - HorizontalResolution drawRes = LodConfig.CLIENT.graphics.qualityOption.drawResolution.get(); - - // apply distance based rules for dynamic overdraw - if (overdraw == VanillaOverdraw.DYNAMIC - && chunkRenderDist <= MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW) - { - // The vanilla render distance isn't far enough - // for partial skipping to make sense... - if (!lodDim.dimension.hasCeiling() && (drawRes == HorizontalResolution.BLOCK)) - { - // ...and the dimension is open, so we don't have to worry about - // LODs rendering on top of the player, - // and the user is using a high horizontal resolution, - // so the overdraw shouldn't be noticeable - overdraw = VanillaOverdraw.ALWAYS; - } - else - { - // ...but we are underground, so we don't want - // LODs rendering on top of the player, - // Or the user is using a LOW horizontal resolution - // and overdraw would be very noticeable. - overdraw = VanillaOverdraw.NEVER; - } - } - - - // determine the skipping type based - // on the overdraw type - switch (overdraw) - { - case ALWAYS: - // don't skip any positions - return new HashSet<>(); - - case DYNAMIC: - - if (chunkRenderDist > MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW - && chunkRenderDist <= MINIMUM_RENDER_DISTANCE_FOR_FAR_OVERDRAW) - { - // This is a small render distance (but greater than the minimum partial - // distance), skip positions that are greater than 2/3 the render distance - skipRadius = (int) Math.ceil(chunkRenderDist * (2.0/3.0)); - } - else - { - // This is a large render distance. Skip positions that are greater than - // 4/5ths the render distance - skipRadius = (int) Math.ceil(chunkRenderDist * (4.0 / 5.0)); - } - break; - - default: - case BORDER: - case NEVER: - // skip chunks in render distance that are rendered - // by vanilla minecraft - skipRadius = 0; - break; - } - - - // get the chunks that are going to be rendered by Minecraft - HashSet posToSkip = getRenderedChunks(); - - - // remove everything outside the skipRadius, - // if the skipRadius is being used - if (skipRadius != 0) - { - for (int x = centerChunk.getX() - chunkRenderDist; x < centerChunk.getX() + chunkRenderDist; x++) - { - for (int z = centerChunk.getZ() - chunkRenderDist; z < centerChunk.getZ() + chunkRenderDist; z++) - { - if (x <= centerChunk.getX() - skipRadius || x >= centerChunk.getX() + skipRadius - || z <= centerChunk.getZ() - skipRadius || z >= centerChunk.getZ() + skipRadius) - posToSkip.remove(new ChunkPos(x, z)); - - } - } - } - return posToSkip; - } - - - /** - * This method returns the ChunkPos of all chunks that Minecraft - * is going to render this frame.

- *

- * Note: This isn't perfect. It will return some chunks that are outside - * the clipping plane. (For example, if you are high above the ground some chunks - * will be incorrectly added, even though they are outside render range). - */ - public static HashSet getRenderedChunks() - { - HashSet loadedPos = new HashSet<>(); - - // Wow, those are some long names! - - // go through every RenderInfo to get the compiled chunks - WorldRenderer renderer = mc.getLevelRenderer(); - for (WorldRenderer.LocalRenderInformationContainer worldRenderer$LocalRenderInformationContainer : renderer.renderChunks) - { - CompiledChunk compiledChunk = worldRenderer$LocalRenderInformationContainer.chunk.getCompiledChunk(); - if (!compiledChunk.hasNoRenderableLayers()) - { - // add the ChunkPos for every rendered chunk - BlockPos bpos = worldRenderer$LocalRenderInformationContainer.chunk.getOrigin(); - - loadedPos.add(new ChunkPos(bpos)); - } - } - - - return loadedPos; - } - - /** - * This method find if a given chunk is a border chunk of the renderable ones - * @param vanillaRenderedChunks matrix of the vanilla rendered chunks - * @param x relative (to the matrix) x chunk to check - * @param z relative (to the matrix) z chunk to check - * @return true if and only if the chunk is a border of the renderable chunks - */ - public static boolean isBorderChunk(boolean[][] vanillaRenderedChunks, int x, int z) - { - if (x < 0 || z < 0 || x >= vanillaRenderedChunks.length || z >= vanillaRenderedChunks[0].length) - return false; - int tempX; - int tempZ; - for (Direction direction : Box.ADJ_DIRECTIONS) - { - tempX = x + Box.DIRECTION_NORMAL_MAP.get(direction).getX(); - tempZ = z + Box.DIRECTION_NORMAL_MAP.get(direction).getZ(); - if (vanillaRenderedChunks[x][z] || (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length) - && !vanillaRenderedChunks[tempX][tempZ])) - return true; - } - return false; - } -} diff --git a/src/main/java/com/seibel/lod/util/ThreadMapUtil.java b/src/main/java/com/seibel/lod/util/ThreadMapUtil.java deleted file mode 100644 index 9eca7fbf6..000000000 --- a/src/main/java/com/seibel/lod/util/ThreadMapUtil.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL 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 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.util; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box; - -import net.minecraft.util.Direction; - -/** - * Holds data used by specific threads so - * the data doesn't have to be recreated every - * time it is needed. - * - * @author Leonardo Amato - * @version 9-25-2021 - */ -public class ThreadMapUtil -{ - public static final ConcurrentMap threadSingleUpdateMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap threadBuilderArrayMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap threadBuilderVerticalArrayMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap threadVerticalAddDataMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap saveContainer = new ConcurrentHashMap<>(); - public static final ConcurrentMap projectionArrayMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap heightAndDepthMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap singleDataToMergeMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap verticalUpdate = new ConcurrentHashMap<>(); - - - //________________________// - // used in BufferBuilder // - //________________________// - - public static final ConcurrentMap adjShadeDisabled = new ConcurrentHashMap<>(); - public static final ConcurrentMap> adjDataMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap boxMap = new ConcurrentHashMap<>(); - - /** returns the array NOT cleared every time */ - public static boolean[] getAdjShadeDisabledArray() - { - if (!adjShadeDisabled.containsKey(Thread.currentThread().getName()) - || (adjShadeDisabled.get(Thread.currentThread().getName()) == null)) - { - adjShadeDisabled.put(Thread.currentThread().getName(), new boolean[Box.DIRECTIONS.length]); - } - Arrays.fill(adjShadeDisabled.get(Thread.currentThread().getName()), false); - return adjShadeDisabled.get(Thread.currentThread().getName()); - } - - /** returns the array NOT cleared every time */ - public static Map getAdjDataArray(int verticalData) - { - if (!adjDataMap.containsKey(Thread.currentThread().getName()) - || (adjDataMap.get(Thread.currentThread().getName()) == null) - || (adjDataMap.get(Thread.currentThread().getName()).get(Direction.NORTH) == null) - || (adjDataMap.get(Thread.currentThread().getName()).get(Direction.NORTH).length != verticalData)) - { - adjDataMap.put(Thread.currentThread().getName(), new HashMap<>()); - adjDataMap.get(Thread.currentThread().getName()).put(Direction.UP, new long[1]); - adjDataMap.get(Thread.currentThread().getName()).put(Direction.DOWN, new long[1]); - for (Direction direction : Box.ADJ_DIRECTIONS) - adjDataMap.get(Thread.currentThread().getName()).put(direction, new long[verticalData]); - } - else - { - - for (Direction direction : Box.ADJ_DIRECTIONS) - Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(direction), DataPointUtil.EMPTY_DATA); - } - return adjDataMap.get(Thread.currentThread().getName()); - } - - public static Box getBox() - { - if (!boxMap.containsKey(Thread.currentThread().getName()) - || (boxMap.get(Thread.currentThread().getName()) == null)) - { - boxMap.put(Thread.currentThread().getName(), new Box()); - } - boxMap.get(Thread.currentThread().getName()).reset(); - return boxMap.get(Thread.currentThread().getName()); - } - - //________________________// - // used in DataPointUtil // - // mergeVerticalData // - //________________________// - - - //________________________// - // used in DataPointUtil // - // mergeSingleData // - //________________________// - - - - /** returns the array filled with 0's */ - public static long[] getBuilderVerticalArray(int detailLevel) - { - if (!threadBuilderVerticalArrayMap.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMap.get(Thread.currentThread().getName()) == null)) - { - long[][] array = new long[5][]; - int size; - for (int i = 0; i < 5; i++) - { - size = 1 << i; - array[i] = new long[size * size * (DataPointUtil.worldHeight / 2 + 1)]; - } - threadBuilderVerticalArrayMap.put(Thread.currentThread().getName(), array); - } - Arrays.fill(threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel], 0); - return threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel]; - } - - /** returns the array NOT cleared every time */ - public static byte[] getSaveContainer(int detailLevel) - { - if (!saveContainer.containsKey(Thread.currentThread().getName()) || (saveContainer.get(Thread.currentThread().getName()) == null)) - { - byte[][] array = new byte[LodUtil.DETAIL_OPTIONS][]; - int size = 1; - for (int i = LodUtil.DETAIL_OPTIONS - 1; i >= 0; i--) - { - array[i] = new byte[2 + 8 * size * size * DetailDistanceUtil.getMaxVerticalData(i)]; - size = size << 1; - } - saveContainer.put(Thread.currentThread().getName(), array); - } - //Arrays.fill(threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel], 0); - return saveContainer.get(Thread.currentThread().getName())[detailLevel]; - } - - - /** returns the array filled with 0's */ - public static long[] getVerticalDataArray(int arrayLength) - { - if (!threadVerticalAddDataMap.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMap.get(Thread.currentThread().getName()) == null)) - { - threadVerticalAddDataMap.put(Thread.currentThread().getName(), new long[arrayLength]); - } - else - { - Arrays.fill(threadVerticalAddDataMap.get(Thread.currentThread().getName()), 0); - } - return threadVerticalAddDataMap.get(Thread.currentThread().getName()); - } - - - - /** returns the array NOT cleared every time */ - public static short[] getHeightAndDepth(int arrayLength) - { - if (!heightAndDepthMap.containsKey(Thread.currentThread().getName()) || (heightAndDepthMap.get(Thread.currentThread().getName()) == null)) - { - heightAndDepthMap.put(Thread.currentThread().getName(), new short[arrayLength]); - } - return heightAndDepthMap.get(Thread.currentThread().getName()); - } - - - /** returns the array filled with 0's */ - public static long[] getVerticalUpdateArray(int detailLevel) - { - if (!verticalUpdate.containsKey(Thread.currentThread().getName()) || (verticalUpdate.get(Thread.currentThread().getName()) == null)) - { - long[][] array = new long[LodUtil.DETAIL_OPTIONS][]; - for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++) - array[i] = new long[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4]; - verticalUpdate.put(Thread.currentThread().getName(), array); - } - else - { - Arrays.fill(verticalUpdate.get(Thread.currentThread().getName())[detailLevel], 0); - } - return verticalUpdate.get(Thread.currentThread().getName())[detailLevel]; - } - - /** clears all arrays so they will have to be rebuilt */ - public static void clearMaps() - { - adjShadeDisabled.clear(); - adjDataMap.clear(); - boxMap.clear(); - threadSingleUpdateMap.clear(); - threadBuilderArrayMap.clear(); - threadBuilderVerticalArrayMap.clear(); - threadVerticalAddDataMap.clear(); - saveContainer.clear(); - projectionArrayMap.clear(); - heightAndDepthMap.clear(); - singleDataToMergeMap.clear(); - verticalUpdate.clear(); - } -} diff --git a/src/main/java/com/seibel/lod/wrappers/Block/BlockPosWrapper.java b/src/main/java/com/seibel/lod/wrappers/Block/BlockPosWrapper.java deleted file mode 100644 index a5e89fd81..000000000 --- a/src/main/java/com/seibel/lod/wrappers/Block/BlockPosWrapper.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.seibel.lod.wrappers.Block; - -import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import java.util.Objects; - -public class BlockPosWrapper { - private final BlockPos.Mutable blockPos; - - - public BlockPosWrapper() - { - this.blockPos = new BlockPos.Mutable(0,0,0); - } - - public BlockPosWrapper(int x, int y, int z) - { - this.blockPos = new BlockPos.Mutable(x, y, z); - } - - public void set(int x, int y, int z) - { - blockPos.set(x, y, z); - } - - public int getX() - { - return blockPos.getX(); - } - - public int getY() - { - return blockPos.getY(); - } - - public int getZ() - { - return blockPos.getZ(); - } - - public int get(Direction.Axis axis) - { - return axis.choose(getX(), getY(), getZ()); - } - - public BlockPos.Mutable getBlockPos() - { - return blockPos; - } - - @Override public boolean equals(Object o) - { - return blockPos.equals(o); - } - - @Override public int hashCode() - { - return Objects.hash(blockPos); - } - - public BlockPosWrapper offset(int x, int y, int z) - { - blockPos.set(blockPos.getX() + x, blockPos.getY() + y, blockPos.getZ() + z); - return this; - } - -} diff --git a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkGenerator.java b/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkGenerator.java deleted file mode 100644 index 44be855ae..000000000 --- a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkGenerator.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.seibel.lod.wrappers.Chunk; - - -//This class will contain all methods usefull to generate the fake ChunkWrapper -public class ChunkGenerator -{ -} diff --git a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkPosWrapper.java b/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkPosWrapper.java deleted file mode 100644 index 2e6ff6723..000000000 --- a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkPosWrapper.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.seibel.lod.wrappers.Chunk; - -import com.seibel.lod.wrappers.Block.BlockPosWrapper; - -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; - -import java.util.Objects; - - -//This class wraps the minecraft ChunkPos class -public class ChunkPosWrapper -{ - private final ChunkPos chunkPos; - - public ChunkPosWrapper(ChunkPos newChunkPos) - { - this.chunkPos = newChunkPos; - } - - public ChunkPosWrapper(BlockPos blockPos) - { - this.chunkPos = new ChunkPos(blockPos); - } - - - public ChunkPosWrapper(ChunkPosWrapper newChunkPos) - { - this.chunkPos = newChunkPos.chunkPos; - } - - public ChunkPosWrapper(BlockPosWrapper blockPos) - { - this.chunkPos = new ChunkPos(blockPos.getBlockPos()); - } - - public ChunkPosWrapper(int chunkX, int chunkZ) - { - this.chunkPos = new ChunkPos(chunkX, chunkZ); - } - - public int getX() - { - return chunkPos.x; - } - - public int getZ() - { - return chunkPos.z; - } - - public int getMinBlockX() - { - return chunkPos.getMinBlockX(); - } - - public int getMinBlockZ() - { - return chunkPos.getMinBlockZ(); - } - - public int getRegionX() - { - return chunkPos.getRegionX(); - } - - public int getRegionZ() - { - return chunkPos.getRegionZ(); - } - - public ChunkPos getChunkPos() - { - return chunkPos; - } - - @Override public boolean equals(Object o) - { - return chunkPos.equals(o); - } - - @Override public int hashCode() - { - return Objects.hash(chunkPos); - } - - public BlockPosWrapper getWorldPosition() - { - BlockPos blockPos = chunkPos.getWorldPosition(); - return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ()); - } -} diff --git a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkWrapper.java b/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkWrapper.java deleted file mode 100644 index 6f36a5500..000000000 --- a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkWrapper.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.seibel.lod.wrappers.Chunk; - -import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.Block.BlockColorWrapper; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import com.seibel.lod.wrappers.Block.BlockShapeWrapper; -import com.seibel.lod.wrappers.World.BiomeWrapper; -import net.minecraft.block.BlockState; -import net.minecraft.block.ILiquidContainer; -import net.minecraft.block.IWaterLoggable; -import net.minecraft.state.properties.BlockStateProperties; -import net.minecraft.world.chunk.IChunk; - -public class ChunkWrapper -{ - - private final IChunk chunk; - private final ChunkPosWrapper chunkPos; - - public int getHeight(){ - return chunk.getMaxBuildHeight(); - } - - public boolean isPositionInWater(BlockPosWrapper blockPos) - { - BlockState blockState = chunk.getBlockState(blockPos.getBlockPos()); - - //This type of block is always in water - return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable)) - || (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)); - } - - public int getHeightMapValue(int xRel, int zRel){ - return chunk.getOrCreateHeightmapUnprimed(LodUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel); - } - - public BiomeWrapper getBiome(int xRel, int yAbs, int zRel) - { - return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(xRel >> 2, yAbs >> 2, zRel >> 2)); - } - - public BlockColorWrapper getBlockColorWrapper(BlockPosWrapper blockPos) - { - return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(blockPos.getBlockPos()).getBlock()); - } - - public BlockShapeWrapper getBlockShapeWrapper(BlockPosWrapper blockPos) - { - return BlockShapeWrapper.getBlockShapeWrapper(chunk.getBlockState(blockPos.getBlockPos()).getBlock(), this, blockPos); - } - - public ChunkWrapper(IChunk chunk) - { - this.chunk = chunk; - this.chunkPos = new ChunkPosWrapper(chunk.getPos()); - } - - public IChunk getChunk(){ - return chunk; - } - public ChunkPosWrapper getPos(){ - return chunkPos; - } - - public boolean isLightCorrect(){ - return chunk.isLightCorrect(); - } - - public boolean - isWaterLogged(BlockPosWrapper blockPos) - { - BlockState blockState = chunk.getBlockState(blockPos.getBlockPos()); - - //This type of block is always in water - return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable)) - || (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)); - } - - public int getEmittedBrightness(BlockPosWrapper blockPos) - { - return chunk.getLightEmission(blockPos.getBlockPos()); - } -} diff --git a/src/main/java/com/seibel/lod/wrappers/LightMapWrapper.java b/src/main/java/com/seibel/lod/wrappers/LightMapWrapper.java deleted file mode 100644 index 9831a5a95..000000000 --- a/src/main/java/com/seibel/lod/wrappers/LightMapWrapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seibel.lod.wrappers; - -import net.minecraft.client.renderer.texture.NativeImage; - - -public class LightMapWrapper -{ - static NativeImage lightMap = null; - - public static void setLightMap(NativeImage newlightMap) - { - lightMap = newlightMap; - } - - public static int getLightValue(int skyLight, int blockLight) - { - return lightMap.getPixelRGBA(skyLight, blockLight); - } -} diff --git a/src/main/java/com/seibel/lod/wrappers/Vertex/BufferBuilderWrapper.java b/src/main/java/com/seibel/lod/wrappers/Vertex/BufferBuilderWrapper.java deleted file mode 100644 index 672c484d6..000000000 --- a/src/main/java/com/seibel/lod/wrappers/Vertex/BufferBuilderWrapper.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.seibel.lod.wrappers.Vertex; - -public class BufferBuilderWrapper -{ -} diff --git a/src/main/java/com/seibel/lod/wrappers/Vertex/VertexBufferWrapper.java b/src/main/java/com/seibel/lod/wrappers/Vertex/VertexBufferWrapper.java deleted file mode 100644 index c7fa61f00..000000000 --- a/src/main/java/com/seibel/lod/wrappers/Vertex/VertexBufferWrapper.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.seibel.lod.wrappers.Vertex; - -public class VertexBufferWrapper -{ -} diff --git a/src/main/java/com/seibel/lod/wrappers/World/BiomeColorWrapper.java b/src/main/java/com/seibel/lod/wrappers/World/BiomeColorWrapper.java deleted file mode 100644 index 15762db9c..000000000 --- a/src/main/java/com/seibel/lod/wrappers/World/BiomeColorWrapper.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.seibel.lod.wrappers.World; - -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import net.minecraft.world.biome.BiomeColors; - - -public class BiomeColorWrapper -{ - - public static int getGrassColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper) - { - return BiomeColors.getAverageGrassColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos()); - } - public static int getWaterColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper) - { - - return BiomeColors.getAverageWaterColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos()); - } - public static int getFoliageColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper) - { - - return BiomeColors.getAverageFoliageColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos()); - } -} diff --git a/src/main/java/com/seibel/lod/wrappers/World/LevelWrapper.java b/src/main/java/com/seibel/lod/wrappers/World/LevelWrapper.java deleted file mode 100644 index 17c35c352..000000000 --- a/src/main/java/com/seibel/lod/wrappers/World/LevelWrapper.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.seibel.lod.wrappers.World; - -import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import net.minecraft.world.IWorld; -import net.minecraft.world.LightType; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -public class LevelWrapper -{ - private static final ConcurrentMap worldWrapperMap = new ConcurrentHashMap<>(); - private final IWorld world; - - public LevelWrapper(IWorld world) - { - this.world = world; - } - - - public static LevelWrapper getLevelWrapper(IWorld world) - { - //first we check if the biome has already been wrapped - if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null) - return worldWrapperMap.get(world); - - - //if it hasn't been created yet, we create it and save it in the map - LevelWrapper levelWrapper = new LevelWrapper(world); - worldWrapperMap.put(world, levelWrapper); - - //we return the newly created wrapper - return levelWrapper; - } - - public static void clearMap() - { - worldWrapperMap.clear(); - } - - public DimensionTypeWrapper getDimensionType() - { - return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType()); - } - - public int getBlockLight(BlockPosWrapper blockPos) - { - return world.getBrightness(LightType.BLOCK, blockPos.getBlockPos()); - } - - public int getSkyLight(BlockPosWrapper blockPos) - { - return world.getBrightness(LightType.SKY, blockPos.getBlockPos()); - } - - public BiomeWrapper getBiome(BlockPosWrapper blockPos) - { - return BiomeWrapper.getBiomeWrapper(world.getBiome(blockPos.getBlockPos())); - } - - public IWorld getWorld() - { - return world; - } - - public boolean hasCeiling() - { - return world.dimensionType().hasCeiling(); - } - - public boolean hasSkyLight() - { - return world.dimensionType().hasSkyLight(); - } - - public boolean isEmpty() - { - return world == null; - } -} diff --git a/src/main/java/com/seibel/lod/wrappers/World/WorldLightWrapper.java b/src/main/java/com/seibel/lod/wrappers/World/WorldLightWrapper.java deleted file mode 100644 index e3a4483f6..000000000 --- a/src/main/java/com/seibel/lod/wrappers/World/WorldLightWrapper.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.seibel.lod.wrappers.World; - - -//We will use this class to get all the light information from the game like skylight, blocklight and light emission; -public class WorldLightWrapper -{ -} diff --git a/src/main/resources/lod.mixins.json b/src/main/resources/lod.mixins.json index d56b32f3a..dd0878cf7 100644 --- a/src/main/resources/lod.mixins.json +++ b/src/main/resources/lod.mixins.json @@ -1,6 +1,6 @@ { "required": true, - "package": "com.seibel.lod.mixin", + "package": "com.seibel.lod.forge.mixins", "compatibilityLevel": "JAVA_8", "refmap": "lod.refmap.json", "mixins": [