From 5c47170ef49f4fa3518c570a117959a3c5be31ee Mon Sep 17 00:00:00 2001 From: coolGi2007 Date: Thu, 9 Dec 2021 06:14:30 +0000 Subject: [PATCH] Now uses Architectury and added Forge support --- .gitattributes | 5 + .gitignore | 64 +- .gitmodules | 3 + Readme.md | 2 - build.gradle | 169 +-- common/build.gradle | 36 + .../java/com/seibel/lod/common/Config.java | 20 +- .../com/seibel/lod/common/LodCommonMain.java | 29 + .../common/forge/LodForgeMethodCaller.java | 0 .../lod/common/wrappers/DependencySetup.java | 0 .../common/wrappers/McObjectConverter.java | 0 .../lod/common/wrappers/WrapperFactory.java | 0 .../lod/common/wrappers/WrapperUtil.java | 0 .../block/BlockColorSingletonWrapper.java | 0 .../wrappers/block/BlockColorWrapper.java | 0 .../wrappers/block/BlockPosWrapper.java | 0 .../wrappers/block/BlockShapeWrapper.java | 0 .../block/TextureAtlasSpriteWrapper.java | 0 .../wrappers/chunk/ChunkPosWrapper.java | 0 .../common/wrappers/chunk/ChunkWrapper.java | 0 .../lod/common/wrappers/config/ConfigGui.java | 65 +- .../config/LodConfigWrapperSingleton.java | 0 .../wrappers/config/TexturedButtonWidget.java | 0 .../minecraft/MinecraftRenderWrapper.java | 135 ++- .../wrappers/minecraft/MinecraftWrapper.java | 25 +- .../wrappers/minecraft/ProfilerWrapper.java | 0 .../common/wrappers/misc/LightMapWrapper.java | 0 .../world/BiomeColorWrapperSingleton.java | 0 .../common/wrappers/world/BiomeWrapper.java | 0 .../wrappers/world/DimensionTypeWrapper.java | 0 .../common/wrappers/world/WorldWrapper.java | 0 .../worldGeneration/LodServerWorld.java | 31 +- .../WorldGeneratorWrapper.java | 0 .../main/resources/assets/lod/lang/en_us.json | 6 +- .../assets/lod/textures/gui/button.png | Bin {src => common/src}/main/resources/icon.png | Bin .../src}/main/resources/lod.accesswidener | 0 common/src/main/resources/logo.png | Bin 0 -> 175826 bytes fabric/build.gradle | 130 ++ .../com/seibel/lod/fabric/ClientProxy.java | 0 .../main/java/com/seibel/lod/fabric/Main.java | 2 - .../lod/fabric/mixins/MixinMinecraft.java | 0 .../lod/fabric/mixins/MixinOptionsScreen.java | 0 .../lod/fabric/mixins/MixinWorldRenderer.java | 14 +- .../mixins/events/MixinClientLevel.java | 0 .../fabric/mixins/events/MixinMinecraft.java | 0 .../mixins/events/MixinServerLevel.java | 0 .../lod/fabric/wrappers/DependencySetup.java | 0 .../wrappers/config/ModMenuIntegration.java | 0 .../src}/main/resources/fabric.mod.json | 0 .../src}/main/resources/lod.mixins.json | 2 +- forge/build.gradle | 107 ++ forge/gradle.properties | 1 + .../seibel/lod/forge/ForgeClientProxy.java | 107 ++ .../java/com/seibel/lod/forge/ForgeMain.java | 90 ++ .../lod/forge/mixins/MixinOptionsScreen.java | 48 + .../lod/forge/mixins/MixinWorldRenderer.java | 72 ++ .../forge/wrappers/ForgeDependencySetup.java | 23 + forge/src/main/resources/META-INF/mods.toml | 51 + forge/src/main/resources/lod.mixins.json | 11 + forge/src/main/resources/pack.mcmeta | 6 + gradle.properties | 18 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 257 ++-- .../com/seibel/lod/common/LodCommonMain.java | 40 - .../java/com/seibel/lod/core/ModInfo.java | 42 - .../com/seibel/lod/core/api/ApiShared.java | 53 - .../com/seibel/lod/core/api/ClientApi.java | 190 --- .../com/seibel/lod/core/api/EventApi.java | 244 ---- .../LodBufferBuilderFactory.java | 1062 ----------------- .../lodTemplates/AbstractLodTemplate.java | 52 - .../lodTemplates/CubicLodTemplate.java | 142 --- .../lodTemplates/DynamicLodTemplate.java | 48 - .../lodTemplates/TriangularLodTemplate.java | 46 - .../core/builders/lodBuilding/LodBuilder.java | 541 --------- .../lodBuilding/LodBuilderConfig.java | 95 -- .../worldGeneration/LodGenWorker.java | 212 ---- .../worldGeneration/LodWorldGenerator.java | 209 ---- .../lod/core/dataFormat/BlockDataFormat.java | 5 - .../lod/core/dataFormat/ColorFormat.java | 5 - .../core/dataFormat/DepthHeightFormat.java | 5 - .../lod/core/dataFormat/LightFormat.java | 5 - .../core/dataFormat/PositionDataFormat.java | 5 - .../seibel/lod/core/enums/LodDirection.java | 529 -------- .../com/seibel/lod/core/enums/WorldType.java | 33 - .../lod/core/enums/config/BlocksToAvoid.java | 47 - .../core/enums/config/BufferRebuildTimes.java | 52 - .../enums/config/DistanceGenerationMode.java | 95 -- .../core/enums/config/GenerationPriority.java | 41 - .../core/enums/config/GpuUploadMethod.java | 59 - .../core/enums/config/HorizontalQuality.java | 53 - .../enums/config/HorizontalResolution.java | 175 --- .../core/enums/config/HorizontalScale.java | 49 - .../lod/core/enums/config/LodTemplate.java | 62 - .../lod/core/enums/config/ShadingMode.java | 22 - .../core/enums/config/VanillaOverdraw.java | 45 - .../core/enums/config/VerticalQuality.java | 80 -- .../lod/core/enums/rendering/DebugMode.java | 54 - .../core/enums/rendering/FogColorMode.java | 42 - .../lod/core/enums/rendering/FogDistance.java | 33 - .../lod/core/enums/rendering/FogDrawMode.java | 40 - .../core/enums/rendering/GLProxyContext.java | 41 - .../lod/core/handlers/ChunkFileLoader.java | 65 - .../lod/core/handlers/IReflectionHandler.java | 58 - .../handlers/LodDimensionFileHandler.java | 485 -------- .../lod/core/handlers/ReflectionHandler.java | 195 --- .../lod/core/objects/MinDefaultMax.java | 41 - .../core/objects/PosToGenerateContainer.java | 209 ---- .../core/objects/PosToRenderContainer.java | 147 --- .../lod/core/objects/VertexOptimizer.java | 626 ---------- .../lod/core/objects/lod/LevelContainer.java | 115 -- .../lod/core/objects/lod/LodDimension.java | 913 -------------- .../lod/core/objects/lod/LodRegion.java | 611 ---------- .../seibel/lod/core/objects/lod/LodWorld.java | 171 --- .../lod/core/objects/lod/RegionPos.java | 91 -- .../objects/lod/VerticalLevelContainer.java | 289 ----- .../seibel/lod/core/objects/math/Mat4f.java | 543 --------- .../seibel/lod/core/objects/math/Vec3d.java | 257 ---- .../seibel/lod/core/objects/math/Vec3f.java | 261 ---- .../seibel/lod/core/objects/math/Vec3i.java | 203 ---- .../opengl/DefaultLodVertexFormats.java | 48 - .../core/objects/opengl/LodBufferBuilder.java | 543 --------- .../core/objects/opengl/LodVertexBuffer.java | 57 - .../core/objects/opengl/LodVertexFormat.java | 186 --- .../opengl/LodVertexFormatElement.java | 161 --- .../core/objects/rending/LodFogConfig.java | 43 - .../com/seibel/lod/core/render/GLProxy.java | 426 ------- .../seibel/lod/core/render/LodRenderer.java | 777 ------------ .../seibel/lod/core/render/RenderUtil.java | 124 -- .../lod/core/render/shader/LodShader.java | 124 -- .../core/render/shader/LodShaderProgram.java | 203 ---- .../com/seibel/lod/core/util/ColorUtil.java | 122 -- .../seibel/lod/core/util/DataPointUtil.java | 521 -------- .../lod/core/util/DetailDistanceUtil.java | 174 --- .../seibel/lod/core/util/LevelPosUtil.java | 269 ----- .../lod/core/util/LodThreadFactory.java | 46 - .../com/seibel/lod/core/util/LodUtil.java | 427 ------- .../lod/core/util/SingletonHandler.java | 98 -- .../seibel/lod/core/util/ThreadMapUtil.java | 218 ---- .../wrapperInterfaces/IWrapperFactory.java | 48 - .../block/AbstractBlockPosWrapper.java | 48 - .../block/IBlockColorSingletonWrapper.java | 35 - .../block/IBlockColorWrapper.java | 50 - .../block/IBlockShapeWrapper.java | 39 - .../chunk/AbstractChunkPosWrapper.java | 52 - .../chunk/IChunkWrapper.java | 52 - .../config/ILodConfigWrapperSingleton.java | 512 -------- .../minecraft/IMinecraftRenderWrapper.java | 69 -- .../minecraft/IMinecraftWrapper.java | 157 --- .../minecraft/IProfilerWrapper.java | 33 - .../misc/ILightMapWrapper.java | 29 - .../world/IBiomeColorWrapperSingleton.java | 38 - .../world/IBiomeWrapper.java | 37 - .../world/IDimensionTypeWrapper.java | 33 - .../world/IWorldWrapper.java | 61 - .../AbstractWorldGeneratorWrapper.java | 50 - src/main/resources/shaders/flat_shaded.frag | 89 -- src/main/resources/shaders/standard.vert | 34 - 158 files changed, 1128 insertions(+), 16269 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitmodules create mode 100644 common/build.gradle rename {src => common/src}/main/java/com/seibel/lod/common/Config.java (94%) create mode 100644 common/src/main/java/com/seibel/lod/common/LodCommonMain.java rename {src => common/src}/main/java/com/seibel/lod/common/forge/LodForgeMethodCaller.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/DependencySetup.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/McObjectConverter.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/WrapperUtil.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/block/BlockColorSingletonWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/block/BlockColorWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/block/BlockPosWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/block/BlockShapeWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/block/TextureAtlasSpriteWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/chunk/ChunkPosWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java (93%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/config/LodConfigWrapperSingleton.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/config/TexturedButtonWidget.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java (50%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java (93%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/minecraft/ProfilerWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/misc/LightMapWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/world/BiomeColorWrapperSingleton.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/world/BiomeWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/world/DimensionTypeWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/world/WorldWrapper.java (100%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java (99%) rename {src => common/src}/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java (100%) rename {src => common/src}/main/resources/assets/lod/lang/en_us.json (97%) rename {src => common/src}/main/resources/assets/lod/textures/gui/button.png (100%) rename {src => common/src}/main/resources/icon.png (100%) rename {src => common/src}/main/resources/lod.accesswidener (100%) create mode 100644 common/src/main/resources/logo.png create mode 100644 fabric/build.gradle rename {src => fabric/src}/main/java/com/seibel/lod/fabric/ClientProxy.java (100%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/Main.java (98%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/mixins/MixinMinecraft.java (100%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/mixins/MixinOptionsScreen.java (100%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/mixins/MixinWorldRenderer.java (84%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/mixins/events/MixinClientLevel.java (100%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/mixins/events/MixinMinecraft.java (100%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/mixins/events/MixinServerLevel.java (100%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/wrappers/DependencySetup.java (100%) rename {src => fabric/src}/main/java/com/seibel/lod/fabric/wrappers/config/ModMenuIntegration.java (100%) rename {src => fabric/src}/main/resources/fabric.mod.json (100%) rename {src => fabric/src}/main/resources/lod.mixins.json (90%) create mode 100644 forge/build.gradle create mode 100644 forge/gradle.properties create mode 100644 forge/src/main/java/com/seibel/lod/forge/ForgeClientProxy.java create mode 100644 forge/src/main/java/com/seibel/lod/forge/ForgeMain.java create mode 100644 forge/src/main/java/com/seibel/lod/forge/mixins/MixinOptionsScreen.java create mode 100644 forge/src/main/java/com/seibel/lod/forge/mixins/MixinWorldRenderer.java create mode 100644 forge/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java create mode 100644 forge/src/main/resources/META-INF/mods.toml create mode 100644 forge/src/main/resources/lod.mixins.json create mode 100644 forge/src/main/resources/pack.mcmeta delete mode 100644 src/main/java/com/seibel/lod/common/LodCommonMain.java delete mode 100644 src/main/java/com/seibel/lod/core/ModInfo.java delete mode 100644 src/main/java/com/seibel/lod/core/api/ApiShared.java delete mode 100644 src/main/java/com/seibel/lod/core/api/ClientApi.java delete mode 100644 src/main/java/com/seibel/lod/core/api/EventApi.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilderConfig.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/worldGeneration/LodGenWorker.java delete mode 100644 src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java delete mode 100644 src/main/java/com/seibel/lod/core/dataFormat/BlockDataFormat.java delete mode 100644 src/main/java/com/seibel/lod/core/dataFormat/ColorFormat.java delete mode 100644 src/main/java/com/seibel/lod/core/dataFormat/DepthHeightFormat.java delete mode 100644 src/main/java/com/seibel/lod/core/dataFormat/LightFormat.java delete mode 100644 src/main/java/com/seibel/lod/core/dataFormat/PositionDataFormat.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/LodDirection.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/WorldType.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/BlocksToAvoid.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/BufferRebuildTimes.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/DistanceGenerationMode.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/GenerationPriority.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/GpuUploadMethod.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/HorizontalQuality.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/HorizontalResolution.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/HorizontalScale.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/LodTemplate.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/ShadingMode.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/VanillaOverdraw.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/config/VerticalQuality.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/rendering/DebugMode.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/rendering/FogColorMode.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/rendering/FogDistance.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/rendering/FogDrawMode.java delete mode 100644 src/main/java/com/seibel/lod/core/enums/rendering/GLProxyContext.java delete mode 100644 src/main/java/com/seibel/lod/core/handlers/ChunkFileLoader.java delete mode 100644 src/main/java/com/seibel/lod/core/handlers/IReflectionHandler.java delete mode 100644 src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHandler.java delete mode 100644 src/main/java/com/seibel/lod/core/handlers/ReflectionHandler.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/MinDefaultMax.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/PosToGenerateContainer.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/PosToRenderContainer.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/VertexOptimizer.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/lod/LevelContainer.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/lod/LodWorld.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/lod/RegionPos.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/math/Mat4f.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/math/Vec3d.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/math/Vec3f.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/math/Vec3i.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/opengl/DefaultLodVertexFormats.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/opengl/LodBufferBuilder.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/opengl/LodVertexFormat.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/opengl/LodVertexFormatElement.java delete mode 100644 src/main/java/com/seibel/lod/core/objects/rending/LodFogConfig.java delete mode 100644 src/main/java/com/seibel/lod/core/render/GLProxy.java delete mode 100644 src/main/java/com/seibel/lod/core/render/LodRenderer.java delete mode 100644 src/main/java/com/seibel/lod/core/render/RenderUtil.java delete mode 100644 src/main/java/com/seibel/lod/core/render/shader/LodShader.java delete mode 100644 src/main/java/com/seibel/lod/core/render/shader/LodShaderProgram.java delete mode 100644 src/main/java/com/seibel/lod/core/util/ColorUtil.java delete mode 100644 src/main/java/com/seibel/lod/core/util/DataPointUtil.java delete mode 100644 src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java delete mode 100644 src/main/java/com/seibel/lod/core/util/LevelPosUtil.java delete mode 100644 src/main/java/com/seibel/lod/core/util/LodThreadFactory.java delete mode 100644 src/main/java/com/seibel/lod/core/util/LodUtil.java delete mode 100644 src/main/java/com/seibel/lod/core/util/SingletonHandler.java delete mode 100644 src/main/java/com/seibel/lod/core/util/ThreadMapUtil.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/block/AbstractBlockPosWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockColorSingletonWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockColorWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockShapeWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/chunk/AbstractChunkPosWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/chunk/IChunkWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IProfilerWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/misc/ILightMapWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IBiomeColorWrapperSingleton.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IBiomeWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IDimensionTypeWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IWorldWrapper.java delete mode 100644 src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java delete mode 100644 src/main/resources/shaders/flat_shaded.frag delete mode 100644 src/main/resources/shaders/standard.vert diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..f811f6ae6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Disable autocrlf on generated files, they always generate with LF +# Add any extra files or paths here to make git stop saying they +# are changed when only line endings change. +src/generated/**/.cache/cache text eol=lf +src/generated/**/*.json text eol=lf diff --git a/.gitignore b/.gitignore index 09cd281f9..5c1c9cc5b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,33 +1,49 @@ -# gradle - -.gradle/ -build/ -out/ -classes/ - # eclipse - +bin *.launch - -# idea - -.idea/ -*.iml -*.ipr -*.iws - -# vscode - -.settings/ -.vscode/ -bin/ +.settings +.metadata .classpath .project -# macos +# idea +out +*.ipr +*.iws +*.iml +.idea -*.DS_Store +# gradle +build +.gradle -# fabric +# other +eclipse +run +# Files from Forge MDK +logs +forge*changelog.txt + +.architectury-transformer/ +build/ +*.ipr run/ +*.iws +out/ +*.iml +.gradle/ +output/ +bin/ +libs/ + +.classpath +.project +.idea/ +classes/ +.metadata +.vscode +.settings +*.launch + +**/src/generated/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..17c60bca6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "core"] + path = core + url = https://gitlab.com/jeseibel/distant-horizons-core.git diff --git a/Readme.md b/Readme.md index d62b1b0cd..0995e11f8 100644 --- a/Readme.md +++ b/Readme.md @@ -7,8 +7,6 @@ allowing for an increased view distance without harming performance. Or in other words: this mod lets you see farther without turning your game into a slide show.\ If you want to see a quick demo, check out a video covering the mod here: -# WARNING: This is a very early version of the 1.18 version, EXPECT BUGS - ![Minecraft Level Of Detail (LOD) mod - Alpha 1.5](https://i.ytimg.com/vi_webp/H2tnvEVbO1c/mqdefault.webp) Fabric version: 0.12.6\ diff --git a/build.gradle b/build.gradle index 9001ad03c..a4a18c7d9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,138 +1,65 @@ plugins { - id 'fabric-loom' version '0.10-SNAPSHOT' - id 'maven-publish' - id "com.github.johnrengelman.shadow" version "7.1.0" + id "architectury-plugin" version "3.4-SNAPSHOT" + id "dev.architectury.loom" version "0.10.0-SNAPSHOT" apply false } -version = '1.0' -sourceCompatibility = JavaVersion.VERSION_17 -targetCompatibility = JavaVersion.VERSION_17 - -println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) - -// Include resources generated by data generators. -sourceSets.main.resources { srcDir 'src/generated/resources' } - -loom { - accessWidenerPath = file("src/main/resources/lod.accesswidener") +architectury { + minecraft = rootProject.minecraft_version } -// this is required so that we can use -// jitpack in the dependencies section below -repositories { - mavenCentral() - // used to download and compile dependencies from git repos - maven { url 'https://jitpack.io' } +subprojects { p -> + apply plugin: "dev.architectury.loom" - // Required for ModMenu - maven { url "https://maven.terraformersmc.com/" } -} - -configurations { - shadowMe - compileOnly.extendsFrom(embed) -} - -dependencies { - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings loom.officialMojangMappings() - modImplementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}" - - // Fabric API - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" - - // Mod Menu - modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") { - exclude(group: "net.fabricmc.fabric-api") + loom { + silentMojangMappingsLicense() } - implementation 'org.tukaani:xz:1.9' - shadowMe 'org.tukaani:xz:1.9' - implementation 'org.apache.commons:commons-compress:1.21' - shadowMe 'org.apache.commons:commons-compress:1.21' -} - -shadowJar { - duplicatesStrategy = DuplicatesStrategy.INCLUDE - configurations = [project.configurations.getByName("shadowMe")] - relocate 'org.tukaani', 'shaded.tukaani' - relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress' - archiveClassifier = 'unmapped' - - from("LICENSE") { - rename { "${it}_${project.archivesBaseName}"} + configurations { + common + shadowMe + implementation.extendsFrom shadowMe } -} -jar { - archiveClassifier = 'unmapped' -} + dependencies { + minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" + // The following line declares the mojmap mappings + mappings loom.officialMojangMappings() -remapJar { - dependsOn(shadowJar) - mustRunAfter(shadowJar) - input = shadowJar.archiveFile + if (p != project(":forge")) { + // We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies + // Do NOT use other classes from fabric loader unless working with fabric + modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" + } - archiveBaseName = "${project.name}" - archiveVersion = "${project.version}" - archiveClassifier = '' -} -processResources { - inputs.property "version", project.version - - filesMatching("fabric.mod.json") { - expand "version": project.version - } -} - -tasks.withType(JavaCompile).configureEach { - // ensure that the encoding is set to UTF-8, no matter what the system default is - // this fixes some edge cases with special characters not displaying correctly - // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html - // If Javadoc is generated, this must be specified in that task too. - it.options.encoding = "UTF-8" - - // Minecraft 1.18 (1.18-pre2) upwards uses Java 17. - it.options.release = 17 -} - -java { - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task - // if it is present. - // If you remove this line, sources will not be generated. - withSourcesJar() -} - -// configure the maven publication -publishing { - publications { - mavenJava(MavenPublication) { - // add all the jars that should be included when publishing to maven - artifact(remapJar) { - builtBy remapJar - } - artifact(sourcesJar) { - builtBy remapSourcesJar - } + if (p != project(":core")) { + common(project(":core")) { transitive false } + shadowMe(project(":core")) { transitive false } } } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - // Notice: This block does NOT have the same function as the block in the top level. - // The repositories here will be used for publishing your artifact, not for - // retrieving dependencies. - } } -//// add Distant Horizon's Core source folders -//sourceSets { -// main { -// java { -// srcDirs("core/src/main/java"); -// srcDirs("core/src/main/resources") -// } -// } -//} +allprojects { + apply plugin: "java" + apply plugin: "architectury-plugin" + apply plugin: "maven-publish" + + archivesBaseName = rootProject.archives_base_name + version = rootProject.mod_version + group = rootProject.maven_group + + repositories { + mavenCentral() + // used to download and compile dependencies from git repos + maven { url 'https://jitpack.io' } + } + + tasks.withType(JavaCompile) { + // Minecraft 1.18 (1.18-pre2) upwards uses Java 17. + options.release = 17 + } + + java { + withSourcesJar() + } +} diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 000000000..353f8bd53 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,36 @@ +loom { + accessWidenerPath.set(file("src/main/resources/lod.accesswidener")) +} + +architectury { + common() +} + +afterEvaluate { + tasks { + remapJar { + remapAccessWidener.set(false) + } + } +} + +publishing { + publications { + mavenCommon(MavenPublication) { + artifactId = rootProject.archives_base_name + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + } +} + +dependencies { + implementation("com.moandjiezana.toml:toml4j:${rootProject.toml_version}") + shadowMe("com.moandjiezana.toml:toml4j:${rootProject.toml_version}") { + exclude(module: "gson") + } +} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/common/Config.java b/common/src/main/java/com/seibel/lod/common/Config.java similarity index 94% rename from src/main/java/com/seibel/lod/common/Config.java rename to common/src/main/java/com/seibel/lod/common/Config.java index 7c675143f..84677d1e9 100644 --- a/src/main/java/com/seibel/lod/common/Config.java +++ b/common/src/main/java/com/seibel/lod/common/Config.java @@ -52,37 +52,37 @@ public class Config extends ConfigGui // Since the original config system uses forge stuff, that means we have to rewrite the whole config system @ScreenEntry(to = "client") - public static ScreenEntry client; + public static Client client; public static class Client { @Category("client") @ScreenEntry(to = "graphics") - public static ScreenEntry graphics; + public static Graphics graphics; @Category("client") @ScreenEntry(to = "worldGenerator") - public static ScreenEntry worldGenerator; + public static WorldGenerator worldGenerator; @Category("client") @ScreenEntry(to = "advanced") - public static ScreenEntry advanced; + public static Advanced advanced; public static class Graphics { @Category("client.graphics") @ScreenEntry(to = "quality") - public static ScreenEntry quality; + public static Quality quality; @Category("client.graphics") @ScreenEntry(to = "fogQuality") - public static ScreenEntry fogQuality; + public static FogQuality fogQuality; @Category("client.graphics") @ScreenEntry(to = "advancedGraphics") - public static ScreenEntry advancedGraphics; + public static AdvancedGraphics advancedGraphics; public static class Quality @@ -179,15 +179,15 @@ public class Config extends ConfigGui { @Category("client.advanced") @ScreenEntry(to = "threading") - public static ScreenEntry threading; + public static Threading threading; @Category("client.advanced") @ScreenEntry(to = "debugging") - public static ScreenEntry debugging; + public static Debugging debugging; @Category("client.advanced") @ScreenEntry(to = "buffers") - public static ScreenEntry buffers; + public static Buffers buffers; public static class Threading diff --git a/common/src/main/java/com/seibel/lod/common/LodCommonMain.java b/common/src/main/java/com/seibel/lod/common/LodCommonMain.java new file mode 100644 index 000000000..6ebb2c540 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/LodCommonMain.java @@ -0,0 +1,29 @@ +package com.seibel.lod.common; + +import com.seibel.lod.common.forge.LodForgeMethodCaller; +import com.seibel.lod.common.wrappers.DependencySetup; +import com.seibel.lod.common.wrappers.config.ConfigGui; +import com.seibel.lod.core.ModInfo; + +/** + * This is the common main class + * @author Ran + */ +public class LodCommonMain { + public static boolean forge = false; + public static LodForgeMethodCaller forgeMethodCaller; + + public static void startup(LodForgeMethodCaller caller) { + if (caller != null) { + LodCommonMain.forge = true; + forgeMethodCaller = caller; + } + + DependencySetup.createInitialBindings(); + } + + + public static void initConfig() { + ConfigGui.init(ModInfo.ID, Config.class); + } +} diff --git a/src/main/java/com/seibel/lod/common/forge/LodForgeMethodCaller.java b/common/src/main/java/com/seibel/lod/common/forge/LodForgeMethodCaller.java similarity index 100% rename from src/main/java/com/seibel/lod/common/forge/LodForgeMethodCaller.java rename to common/src/main/java/com/seibel/lod/common/forge/LodForgeMethodCaller.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/DependencySetup.java b/common/src/main/java/com/seibel/lod/common/wrappers/DependencySetup.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/DependencySetup.java rename to common/src/main/java/com/seibel/lod/common/wrappers/DependencySetup.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/McObjectConverter.java b/common/src/main/java/com/seibel/lod/common/wrappers/McObjectConverter.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/McObjectConverter.java rename to common/src/main/java/com/seibel/lod/common/wrappers/McObjectConverter.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java b/common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java rename to common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/WrapperUtil.java b/common/src/main/java/com/seibel/lod/common/wrappers/WrapperUtil.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/WrapperUtil.java rename to common/src/main/java/com/seibel/lod/common/wrappers/WrapperUtil.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorSingletonWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorSingletonWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/block/BlockColorSingletonWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorSingletonWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/block/BlockColorWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/block/BlockColorWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/block/BlockPosWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockPosWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/block/BlockPosWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/block/BlockPosWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/block/BlockShapeWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockShapeWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/block/BlockShapeWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/block/BlockShapeWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/block/TextureAtlasSpriteWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/TextureAtlasSpriteWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/block/TextureAtlasSpriteWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/block/TextureAtlasSpriteWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkPosWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkPosWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkPosWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkPosWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/chunk/ChunkWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java b/common/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java similarity index 93% rename from src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java rename to common/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java index 41b8ab1a4..bb4dde917 100644 --- a/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/config/ConfigGui.java @@ -2,13 +2,10 @@ package com.seibel.lod.common.wrappers.config; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import com.moandjiezana.toml.Toml; +import com.moandjiezana.toml.TomlWriter; import com.mojang.blaze3d.vertex.PoseStack; import com.seibel.lod.core.ModInfo; -//import net.fabricmc.api.EnvType; -//import net.fabricmc.api.Environment; -//import net.fabricmc.loader.api.FabricLoader; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; @@ -26,6 +23,8 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -39,15 +38,14 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; - /** - * which is based upon TinyConfig + * Based upon TinyConfig * https://github.com/Minenash/TinyConfig * * Credits to Motschen * * @author coolGi2007 - * @version 12-06-2021 + * @version 12-09-2021 */ // Everything required is packed into 1 class, so it is easier to copy // This config should work for both Fabric and Forge as long as you use Mojang mappings @@ -106,6 +104,13 @@ public abstract class ConfigGui { To make a textured button to the options screen look in the mixins/MixinOptionsScreen class and TexturedButtonWidget class Remember to add the MixinOptionsScreen to your ModID.mixins.json */ + + /* + This is a small to do list for the config + + Make config save + Add a way to add min and max from another variable + */ private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)"); private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)"); @@ -114,14 +119,14 @@ public abstract class ConfigGui { protected static class EntryInfo { Field field; Object widget; - int width; + int width = 0; int max; Map.Entry error; Object defaultValue; Object value; String tempValue; boolean inLimits = true; - String id; + String id; // ModID TranslatableComponent name; int index; boolean button = false; // This asks if it is a button to goto a new screen @@ -132,28 +137,29 @@ public abstract class ConfigGui { public static final Map> configClass = new HashMap<>(); private static Path path; - private static final Gson gson = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).excludeFieldsWithModifiers(Modifier.PRIVATE).addSerializationExclusionStrategy(new HiddenAnnotationExclusionStrategy()).setPrettyPrinting().create(); - public static void init(String modid, Class config) { - path = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(modid + ".json"); + path = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(modid + ".toml"); configClass.put(modid, config); for (Field field : config.getFields()) { EntryInfo info = new EntryInfo(); if (field.isAnnotationPresent(Entry.class) || field.isAnnotationPresent(Comment.class) || field.isAnnotationPresent(ScreenEntry.class)) - // TODO: Fix the check for client/server + // TODO[CONFIG]: Fix the check for client/server // if (Minecraft.getInstance().getEnvironmentType() == EnvType.CLIENT) initClient(modid, field, info); if (field.isAnnotationPresent(Entry.class)) try { info.defaultValue = field.get(null); } catch (IllegalAccessException ignored) {} + if (field.isAnnotationPresent(ScreenEntry.class)) { + ConfigGui.init(modid, field.getType()); + } + } // File saving stuff - // TODO[CONFIG]: Change to .toml try { - gson.fromJson(Files.newBufferedReader(path), config); + new Toml().read(Files.newBufferedReader(path)).to(config); } catch (Exception e) { write(modid); } @@ -168,11 +174,15 @@ public abstract class ConfigGui { } } private static void initClient(String modid, Field field, EntryInfo info) { + // This adds the buttons to the queue to be rendered Class type = field.getType(); Category c = field.getAnnotation(Category.class); Entry e = field.getAnnotation(Entry.class); ScreenEntry s = field.getAnnotation(ScreenEntry.class); - info.width = e != null ? e.width() : 0; + if (e!=null) + info.width = e.width(); + else if (s!=null) + info.width = s.width(); info.field = field; info.id = modid; info.category = c != null ? c.value() : ""; @@ -180,20 +190,20 @@ public abstract class ConfigGui { if (e != null) { if (!e.name().equals("")) info.name = new TranslatableComponent(e.name()); - if (type == int.class) + if (type == int.class) // For int textField(info, Integer::parseInt, INTEGER_ONLY, e.min(), e.max(), true); - else if (type == double.class) + else if (type == double.class) // For double textField(info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(), false); - else if (type == String.class || type == List.class) { + else if (type == String.class || type == List.class) { // For string or list info.max = e.max() == Double.MAX_VALUE ? Integer.MAX_VALUE : (int) e.max(); textField(info, String::length, null, Math.min(e.min(), 0), Math.max(e.max(), 1), true); - } else if (type == boolean.class) { + } else if (type == boolean.class) { // For boolean Function func = value -> new TextComponent((Boolean) value ? "True" : "False").withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED); info.widget = new AbstractMap.SimpleEntry>(button -> { info.value = !(Boolean) info.value; button.setMessage(func.apply(info.value)); }, func); - } else if (type.isEnum()) { + } else if (type.isEnum()) { // For enum List values = Arrays.asList(field.getType().getEnumConstants()); Function func = value -> new TranslatableComponent(modid + ".config." + "enum." + type.getSimpleName() + "." + info.value.toString()); info.widget = new AbstractMap.SimpleEntry>(button -> { @@ -244,13 +254,13 @@ public abstract class ConfigGui { }; } - // Creates the modid.json + // Creates the modid.toml public static void write(String modid) { - path = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(modid + ".json"); + path = Minecraft.getInstance().gameDirectory.toPath().resolve("config").resolve(modid + ".toml"); try { if (!Files.exists(path)) Files.createFile(path); - Files.write(path, gson.toJson(configClass.get(modid).getDeclaredConstructor().newInstance()).getBytes()); + new TomlWriter().write(configClass.get(modid).getDeclaredConstructor().newInstance(), path.toFile()); } catch (Exception e) { e.printStackTrace(); } @@ -284,7 +294,7 @@ public abstract class ConfigGui { } private void loadValues() { try { - gson.fromJson(Files.newBufferedReader(path), configClass.get(modid)); + new Toml().read(Files.newBufferedReader(path)).to(configClass.get(modid)); } catch (Exception e) { write(modid); } @@ -368,7 +378,7 @@ public abstract class ConfigGui { widget.setFilter(processor); this.list.addButton(widget, resetButton, null, name); } else if (info.button) { - Button widget = new Button(this.width / 2 - 100, this.height - 28, 200, 20, name, (button -> { + Button widget = new Button(this.width / 2 - info.width, this.height - 28, info.width*2, 20, name, (button -> { Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, ModInfo.ID, info.gotoScreen)); })); this.list.addButton(widget, null, null, null); @@ -493,6 +503,7 @@ public abstract class ConfigGui { public @interface ScreenEntry { String to(); String name() default ""; + int width() default 100; } // Where the @Category is defined diff --git a/src/main/java/com/seibel/lod/common/wrappers/config/LodConfigWrapperSingleton.java b/common/src/main/java/com/seibel/lod/common/wrappers/config/LodConfigWrapperSingleton.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/config/LodConfigWrapperSingleton.java rename to common/src/main/java/com/seibel/lod/common/wrappers/config/LodConfigWrapperSingleton.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/config/TexturedButtonWidget.java b/common/src/main/java/com/seibel/lod/common/wrappers/config/TexturedButtonWidget.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/config/TexturedButtonWidget.java rename to common/src/main/java/com/seibel/lod/common/wrappers/config/TexturedButtonWidget.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java similarity index 50% rename from src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java index cb3e608c2..819dbc862 100644 --- a/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java @@ -3,6 +3,10 @@ package com.seibel.lod.common.wrappers.minecraft; import java.awt.*; import java.util.HashSet; +import com.mojang.blaze3d.platform.NativeImage; +import com.seibel.lod.common.wrappers.misc.LightMapWrapper; +import com.seibel.lod.core.util.LodUtil; +import net.minecraft.client.renderer.LightTexture; import org.lwjgl.opengl.GL20; import com.mojang.math.Vector3f; @@ -31,21 +35,22 @@ import net.minecraft.world.phys.Vec3; * related to rendering in Minecraft. * * @author James Seibel - * @version 11-26-2021 + * @version 12-05-2021 */ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper { public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper(); + public static final MinecraftWrapper MC_WRAPPER = MinecraftWrapper.INSTANCE; - private final GameRenderer gameRenderer = Minecraft.getInstance().gameRenderer; - private final static Minecraft mc = Minecraft.getInstance(); + private static final Minecraft MC = Minecraft.getInstance(); + private static final GameRenderer GAME_RENDERER = MC.gameRenderer; @Override public Vec3f getLookAtVector() { - Camera camera = gameRenderer.getMainCamera(); + Camera camera = GAME_RENDERER.getMainCamera(); Vector3f cameraDir = camera.getLookVector(); return new Vec3f(cameraDir.x(), cameraDir.y(), cameraDir.z()); } @@ -53,7 +58,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public AbstractBlockPosWrapper getCameraBlockPosition() { - Camera camera = gameRenderer.getMainCamera(); + Camera camera = GAME_RENDERER.getMainCamera(); BlockPos blockPos = camera.getBlockPosition(); return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ()); } @@ -61,13 +66,13 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public boolean playerHasBlindnessEffect() { - return mc.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null; + return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null; } @Override public Vec3d getCameraExactPosition() { - Camera camera = gameRenderer.getMainCamera(); + Camera camera = GAME_RENDERER.getMainCamera(); Vec3 projectedView = camera.getPosition(); return new Vec3d(projectedView.x, projectedView.y, projectedView.z); @@ -76,13 +81,13 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public Mat4f getDefaultProjectionMatrix(float partialTicks) { - return McObjectConverter.Convert(gameRenderer.getProjectionMatrix(gameRenderer.getFov(gameRenderer.getMainCamera(), partialTicks, true))); + return McObjectConverter.Convert(GAME_RENDERER.getProjectionMatrix(GAME_RENDERER.getFov(GAME_RENDERER.getMainCamera(), partialTicks, true))); } @Override public double getGamma() { - return mc.options.gamma; + return MC.options.gamma; } @Override @@ -94,8 +99,8 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public Color getSkyColor() { - if (mc.level.dimensionType().hasSkyLight()) { - Vec3 colorValues = mc.level.getSkyColor(mc.gameRenderer.getMainCamera().getPosition(), mc.getFrameTime()); + if (MC.level.dimensionType().hasSkyLight()) { + Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), MC.getFrameTime()); return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z); } else return new Color(0, 0, 0); @@ -104,25 +109,25 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public double getFov(float partialTicks) { - return gameRenderer.getFov(gameRenderer.getMainCamera(), partialTicks, true); + return GAME_RENDERER.getFov(GAME_RENDERER.getMainCamera(), partialTicks, true); } /** Measured in chunks */ @Override public int getRenderDistance() { - return mc.options.renderDistance; + return MC.options.renderDistance; } @Override public int getScreenWidth() { - return mc.getWindow().getWidth(); + return MC.getWindow().getWidth(); } @Override public int getScreenHeight() { - return mc.getWindow().getHeight(); + return MC.getWindow().getHeight(); } /** @@ -141,7 +146,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper // Wow, those are some long names! // go through every RenderInfo to get the compiled chunks - LevelRenderer renderer = mc.levelRenderer; + LevelRenderer renderer = MC.levelRenderer; /* for (RenderChunkInfo worldRenderer$LocalRenderInformationContainer : renderer.renderChunks) { @@ -159,4 +164,102 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper return loadedPos; } + + @Override + public int[] getLightmapPixels() + { + LightTexture tex = GAME_RENDERER.lightTexture(); + NativeImage lightMapPixels = tex.lightPixels; + LightMapWrapper lightMap = new LightMapWrapper(lightMapPixels); + + + int lightMapHeight = getLightmapTextureHeight(); + int lightMapWidth = getLightmapTextureWidth(); + + int pixels[] = new int[lightMapWidth * lightMapHeight]; + for (int u = 0; u < lightMapWidth; u++) + { + for (int v = 0; v < lightMapWidth; v++) + { + // this could probably be kept as a int, but + // it is easier to test and see the colors when debugging this way. + // When creating a new release this should be changed to the int version. + Color c = LodUtil.intToColor(lightMap.getLightValue(u, v)); + + // these should both create a totally white image +// int col = +// Integer.MAX_VALUE; +// int col = +// 0b11111111 + // red +// (0b11111111 << 8) + // green +// (0b11111111 << 16) + // blue +// (0b11111111 << 24); // blue + + int col = + (c.getRed() & 0b11111111) + // red + ((c.getGreen() & 0b11111111) << 8) + // green + ((c.getBlue() & 0b11111111) << 16) + // blue + ((c.getAlpha() & 0b11111111) << 24); // alpha + + // 2D array stored in a 1D array. + // Thank you Tim from College ;) + pixels[u * lightMapWidth + v] = col; + } + } + + return pixels; + } + + + @Override + public int getLightmapTextureHeight() + { + int height = -1; + + LightTexture lightTexture = GAME_RENDERER.lightTexture(); + if (lightTexture != null) + { + NativeImage tex = lightTexture.lightPixels; + if (tex != null) + { + height = tex.getHeight(); + } + } + + return height; + } + + @Override + public int getLightmapTextureWidth() + { + int width = -1; + + LightTexture lightTexture = GAME_RENDERER.lightTexture(); + if (lightTexture != null) + { + NativeImage tex = lightTexture.lightPixels; + if (tex != null) + { + width = tex.getWidth(); + } + } + + return width; + } + + + @Override + public int getLightmapGLFormat() { + int glFormat = -1; + + LightTexture lightTexture = GAME_RENDERER.lightTexture(); + if (lightTexture != null) { + NativeImage tex = lightTexture.lightPixels; + if (tex != null) { + glFormat = tex.format().glFormat(); + } + } + + return glFormat; + } } diff --git a/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java similarity index 93% rename from src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java index f7591e002..35d48b691 100644 --- a/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftWrapper.java @@ -73,7 +73,7 @@ public class MinecraftWrapper implements IMinecraftWrapper { public static final MinecraftWrapper INSTANCE = new MinecraftWrapper(); - private final Minecraft mc = Minecraft.getInstance(); + public final Minecraft mc = Minecraft.getInstance(); /** * The lightmap for the current: @@ -178,31 +178,36 @@ public class MinecraftWrapper implements IMinecraftWrapper /** * Returns the color int at the given pixel coordinates * from the current lightmap. - * @param u x location in texture space - * @param v z location in texture space + * @param blockLight x location in texture space + * @param skyLight z location in texture space */ @Override - public int getColorIntFromLightMap(int u, int v) + public int getColorIntFromLightMap(int blockLight, int skyLight) { if (lightMap == null) { + sendChatMessage("new"); // make sure the lightMap is up-to-date getCurrentLightMap(); } - return lightMap.getPixelRGBA(u, v); + return lightMap.getPixelRGBA(blockLight, skyLight); } /** * Returns the Color at the given pixel coordinates * from the current lightmap. - * @param u x location in texture space - * @param v z location in texture space + * @param blockLight x location in texture space + * @param skyLight z location in texture space */ @Override - public Color getColorFromLightMap(int u, int v) - { - return LodUtil.intToColor(lightMap.getPixelRGBA(u, v)); + public Color getColorFromLightMap(int blockLight, int skyLight) { + if (lightMap == null) { + // make sure the lightMap is up-to-date + getCurrentLightMap(); + } + + return LodUtil.intToColor(lightMap.getPixelRGBA(blockLight, skyLight)); } diff --git a/src/main/java/com/seibel/lod/common/wrappers/minecraft/ProfilerWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/ProfilerWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/minecraft/ProfilerWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/minecraft/ProfilerWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/misc/LightMapWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/misc/LightMapWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/misc/LightMapWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/misc/LightMapWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/world/BiomeColorWrapperSingleton.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeColorWrapperSingleton.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/world/BiomeColorWrapperSingleton.java rename to common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeColorWrapperSingleton.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/world/BiomeWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/world/BiomeWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/world/BiomeWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/world/DimensionTypeWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/DimensionTypeWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/world/DimensionTypeWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/world/DimensionTypeWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/world/WorldWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/world/WorldWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/world/WorldWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/world/WorldWrapper.java diff --git a/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java similarity index 99% rename from src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java rename to common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java index 4d3f9a15f..ed79eb0b3 100644 --- a/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/LodServerWorld.java @@ -137,22 +137,6 @@ public class LodServerWorld implements WorldGenLevel return predicate.test(chunk.getFluidState(blockPos)); } - @Override - public long nextSubTickCount() { - return 0; - } - - @Override - public LevelTickAccess getBlockTicks() - { - return null; - } - - @Override - public LevelTickAccess getFluidTicks() { - return null; - } - @Override public ChunkAccess getChunk(int x, int z, ChunkStatus requiredStatus, boolean nonnull) { @@ -274,6 +258,21 @@ public class LodServerWorld implements WorldGenLevel } + @Override + public long nextSubTickCount() { + return 0; + } + + @Override + public LevelTickAccess getBlockTicks() { + return null; + } + + @Override + public LevelTickAccess getFluidTicks() { + return null; + } + @Override public LevelData getLevelData() { diff --git a/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java similarity index 100% rename from src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java rename to common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java diff --git a/src/main/resources/assets/lod/lang/en_us.json b/common/src/main/resources/assets/lod/lang/en_us.json similarity index 97% rename from src/main/resources/assets/lod/lang/en_us.json rename to common/src/main/resources/assets/lod/lang/en_us.json index afafcd191..e4957b5eb 100644 --- a/src/main/resources/assets/lod/lang/en_us.json +++ b/common/src/main/resources/assets/lod/lang/en_us.json @@ -116,9 +116,5 @@ "lod.config.enum.GpuUploadMethod.DATA": "Data", "lod.config.enum.BufferRebuildTimes.FREQUENT": "Frequent", "lod.config.enum.BufferRebuildTimes.NORMAL": "Normal", - "lod.config.enum.BufferRebuildTimes.RARE": "Rare", - "toast.lod.title": "Distant Horizons", - "key.lod.category": "Distant Horizons", - "key.lod.DebugToggle": "Debug toggle", - "key.lod.DrawToggle": "Draw toggle" + "lod.config.enum.BufferRebuildTimes.RARE": "Rare" } diff --git a/src/main/resources/assets/lod/textures/gui/button.png b/common/src/main/resources/assets/lod/textures/gui/button.png similarity index 100% rename from src/main/resources/assets/lod/textures/gui/button.png rename to common/src/main/resources/assets/lod/textures/gui/button.png diff --git a/src/main/resources/icon.png b/common/src/main/resources/icon.png similarity index 100% rename from src/main/resources/icon.png rename to common/src/main/resources/icon.png diff --git a/src/main/resources/lod.accesswidener b/common/src/main/resources/lod.accesswidener similarity index 100% rename from src/main/resources/lod.accesswidener rename to common/src/main/resources/lod.accesswidener diff --git a/common/src/main/resources/logo.png b/common/src/main/resources/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0caf9c6d1e768674697bf87fca64a9e69ed75a4d GIT binary patch literal 175826 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. + */ + +package com.seibel.lod.forge; + +import com.seibel.lod.core.api.EventApi; +import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.lod.common.wrappers.chunk.ChunkWrapper; +import com.seibel.lod.common.wrappers.world.DimensionTypeWrapper; +import com.seibel.lod.common.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) + { + if (event.getWorld() != null) { + 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/forge/src/main/java/com/seibel/lod/forge/ForgeMain.java b/forge/src/main/java/com/seibel/lod/forge/ForgeMain.java new file mode 100644 index 000000000..aebae6bac --- /dev/null +++ b/forge/src/main/java/com/seibel/lod/forge/ForgeMain.java @@ -0,0 +1,90 @@ +/* + * 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.common.Config; +import com.seibel.lod.common.LodCommonMain; +import com.seibel.lod.common.forge.LodForgeMethodCaller; +import com.seibel.lod.common.wrappers.config.ConfigGui; +import com.seibel.lod.common.wrappers.minecraft.MinecraftWrapper; +import com.seibel.lod.core.ModInfo; +import com.seibel.lod.forge.wrappers.ForgeDependencySetup; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.data.ModelDataMap; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + +import java.util.List; +import java.util.Random; + +/** + * Initialize and setup the Mod.
+ * If you are looking for the real start of the mod + * check out the ClientProxy. + * + * @author James Seibel + * @version 11-21-2021 + */ +@Mod(ModInfo.ID) +public class ForgeMain implements LodForgeMethodCaller +{ + public static ForgeClientProxy forgeClientProxy; + + private void init(final FMLCommonSetupEvent event) + { + // make sure the dependencies are set up before the mod needs them + LodCommonMain.initConfig(); + LodCommonMain.startup(this); + ForgeDependencySetup.createInitialBindings(); + } + + + public ForgeMain() + { + // Register the methods + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientStart); + + // Register ourselves for server and other game events we are interested in + MinecraftForge.EVENT_BUS.register(this); + } + + private void onClientStart(final FMLClientSetupEvent event) + { + forgeClientProxy = new ForgeClientProxy(); + MinecraftForge.EVENT_BUS.register(forgeClientProxy); + } + + private ModelDataMap dataMap = new ModelDataMap.Builder().build(); + @Override + public List getQuads(MinecraftWrapper mc, Block block, BlockState blockState, Direction direction, Random random) { + return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, dataMap); + } +} diff --git a/forge/src/main/java/com/seibel/lod/forge/mixins/MixinOptionsScreen.java b/forge/src/main/java/com/seibel/lod/forge/mixins/MixinOptionsScreen.java new file mode 100644 index 000000000..702590bad --- /dev/null +++ b/forge/src/main/java/com/seibel/lod/forge/mixins/MixinOptionsScreen.java @@ -0,0 +1,48 @@ +package com.seibel.lod.forge.mixins; + +import com.seibel.lod.common.wrappers.config.ConfigGui; +import com.seibel.lod.common.wrappers.config.TexturedButtonWidget; +import com.seibel.lod.core.ModInfo; +import net.minecraft.client.gui.screens.OptionsScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +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 java.util.Objects; + +/** + * Adds a button to the menu to goto the config + * + * @author coolGi2007 + * @version 12-02-2021 + */ +@Mixin(OptionsScreen.class) +public class MixinOptionsScreen extends Screen { + // Get the texture for the button + private static final ResourceLocation ICON_TEXTURE = new ResourceLocation(ModInfo.ID,"textures/gui/button.png"); + protected MixinOptionsScreen(Component title) { + super(title); + } + + @Inject(at = @At("HEAD"),method = "init") + private void lodconfig$init(CallbackInfo ci) { + this.addRenderableWidget(new TexturedButtonWidget( + // Where the button is on the screen + this.width / 2 - 180, this.height / 6 - 12, + // Width and height of the button + 20, 20, + // Offset + 0, 0, + // Some textuary stuff + 20, ICON_TEXTURE, 20, 40, + // Create the button and tell it where to go + (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(ConfigGui.getScreen(this, ModInfo.ID, "client")), + // Add a title to the screen + new TranslatableComponent("text.autoconfig." + ModInfo.ID + ".title"))); + } +} diff --git a/forge/src/main/java/com/seibel/lod/forge/mixins/MixinWorldRenderer.java b/forge/src/main/java/com/seibel/lod/forge/mixins/MixinWorldRenderer.java new file mode 100644 index 000000000..bccfa4ea5 --- /dev/null +++ b/forge/src/main/java/com/seibel/lod/forge/mixins/MixinWorldRenderer.java @@ -0,0 +1,72 @@ +/* + * 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.mixins; + +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.vertex.PoseStack; +import com.mojang.math.Matrix4f; +import com.seibel.lod.core.api.ClientApi; +import com.seibel.lod.core.objects.math.Mat4f; +import com.seibel.lod.common.wrappers.McObjectConverter; + +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.RenderType; + +/** + * This class is used to mix in my rendering code + * before Minecraft starts rendering blocks. + * If this wasn't done, and we used Forge's + * render last event, the LODs would render on top + * of the normal terrain. + * + * @author James Seibel + * @version 9-19-2021 + */ +@Mixin(LevelRenderer.class) +public class MixinWorldRenderer +{ + private static float previousPartialTicks = 0; + + @Inject(at = @At("RETURN"), method = "renderClouds(Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/math/Matrix4f;FDDD)V") + private void renderClouds(PoseStack modelViewMatrixStack, Matrix4f projectionMatrix, float partialTicks, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, CallbackInfo callback) + { + // get the partial ticks since renderChunkLayer doesn't + // have access to them + previousPartialTicks = partialTicks; + } + + // HEAD or RETURN + @Inject(at = @At("HEAD"), method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLcom/mojang/math/Matrix4f;)V") + private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback) + { + // only render before solid blocks + if (renderType.equals(RenderType.solid())) + { + Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose()); + Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix); + + ClientApi.INSTANCE.renderLods(mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks); + } + } +} \ No newline at end of file diff --git a/forge/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java b/forge/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java new file mode 100644 index 000000000..6c18706e7 --- /dev/null +++ b/forge/src/main/java/com/seibel/lod/forge/wrappers/ForgeDependencySetup.java @@ -0,0 +1,23 @@ +package com.seibel.lod.forge.wrappers; + +import com.seibel.lod.common.wrappers.config.LodConfigWrapperSingleton; +import com.seibel.lod.core.util.SingletonHandler; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; + +/** + * 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 + * @author Ran + * @version 12-1-2021 + */ +public class ForgeDependencySetup +{ + public static void createInitialBindings() + { + SingletonHandler.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE); + } +} diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 000000000..0c2028326 --- /dev/null +++ b/forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,51 @@ +#// This is an example mods.toml file. It contains the data relating to the loading mods. +#// There are several mandatory fields (#mandatory), and many more that are optional (#optional). +#// The overall format is standard TOML format, v0.5.0. +#// Note that there are a couple of TOML lists in this file. +#// Find more information on toml format here: https://github.com/toml-lang/toml +#// The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader="javafml" #mandatory + +#// A version range to match for said mod loader - for regular FML @Mod it will be the forge version +loaderVersion="[37,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. + +#// The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +#// Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license="GNU GPLv3" + +#// A URL to refer people to when problems occur with this mod +issueTrackerURL="https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues" #optional +#// A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory + +#// The modid of the mod +modId="lod" #mandatory + +#// The version number of the mod - there's a few well known ${} variables useable here or just hardcode it +#//${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata +#// see the associated build.gradle script for how to populate this completely automatically during a build +version="1.5.4a" #mandatory + +#// A display name for the mod +displayName="Distant Horizons" #mandatory + +#// A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/ +#//updateJSONURL="https://change.me.example.invalid/updates.json" #optional + +#// A URL for the "homepage" for this mod, displayed in the mod UI +displayURL="https://www.curseforge.com/minecraft/mc-mods/lod-level-of-detail" #optional + +#// A file name (in the root of the mod JAR) containing a logo for display +logoFile="logo.png" #optional + +#// A file name (in the root of the mod JAR) containing a icon for display by catalogue +catalogueImageIcon="icon.png" + +#// A text field displayed in the mod UI +credits="TechnoVision, Vike, and Darkhax for their modding tutorials." #optional + +#// A text field displayed in the mod UI +authors="James Seibel, Leonardo Amato, and Cola" #optional + +#// The description text for the mod (multi line!) (#mandatory) +description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.''' diff --git a/forge/src/main/resources/lod.mixins.json b/forge/src/main/resources/lod.mixins.json new file mode 100644 index 000000000..b62798311 --- /dev/null +++ b/forge/src/main/resources/lod.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "com.seibel.lod.forge.mixins", + "compatibilityLevel": "JAVA_17", + "refmap": "lod.refmap.json", + "mixins": [ + "MixinWorldRenderer", + "MixinOptionsScreen" + ], + "minVersion": "0.8" +} \ No newline at end of file diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..70b6321cb --- /dev/null +++ b/forge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "", + "pack_format": 7 + } +} diff --git a/gradle.properties b/gradle.properties index 6befa6a57..bc735486b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,12 +1,14 @@ -# Sets default memory used for gradle commands. Can be overridden by user or command line properties. -# This is required to provide enough memory for the Minecraft decompilation process. -org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false +org.gradle.jvmargs=-Xmx2048M -# Minecraft minecraft_version=1.18 -fabric_loader_version=0.12.6 -# Mod dependencies +archives_base_name=DistantHorizons +mod_version=1.5.4a +maven_group=com.seibel.lod +toml_version=0.7.2 + +fabric_loader_version=0.12.6 fabric_api_version=0.43.1+1.18 -modmenu_version=3.0.0 \ No newline at end of file +modmenu_version=3.0.0 + +forge_version=38.0.14 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a254..e750102e0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c78733..744e882ed 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/bin/sh +#!/usr/bin/env sh # -# Copyright © 2015-2021 the original authors. +# Copyright 2015 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,101 +17,67 @@ # ############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# +## +## Gradle start up script for UN*X +## ############################################################################## # Attempt to set APP_HOME - # Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} +APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum +MAX_FD="maximum" warn () { echo "$*" -} >&2 +} die () { echo echo "$*" echo exit 1 -} >&2 +} # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -121,9 +87,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java + JAVACMD="$JAVA_HOME/jre/sh/java" else - JAVACMD=$JAVA_HOME/bin/java + JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -132,7 +98,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD=java + JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -140,95 +106,80 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi fi -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi # For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=$( cygpath --unix "$JAVACMD" ) + JAVACMD=`cygpath --unix "$JAVACMD"` - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" exec "$JAVACMD" "$@" diff --git a/src/main/java/com/seibel/lod/common/LodCommonMain.java b/src/main/java/com/seibel/lod/common/LodCommonMain.java deleted file mode 100644 index 5cef3285a..000000000 --- a/src/main/java/com/seibel/lod/common/LodCommonMain.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.seibel.lod.common; - -import com.seibel.lod.common.forge.LodForgeMethodCaller; -import com.seibel.lod.common.wrappers.DependencySetup; -import com.seibel.lod.common.wrappers.config.ConfigGui; -import com.seibel.lod.core.ModInfo; - -/** - * This is the common main class - * @author Ran - */ -public class LodCommonMain { - public static boolean forge = false; - public static LodForgeMethodCaller forgeMethodCaller; - - public static void startup(LodForgeMethodCaller caller) { - if (caller != null) { - LodCommonMain.forge = true; - forgeMethodCaller = caller; - } - - DependencySetup.createInitialBindings(); - } - - - // TODO[CONFIG]: Find a better way to initialise everything - public static void initConfig() { - ConfigGui.init(ModInfo.ID, Config.class); - ConfigGui.init(ModInfo.ID, Config.Client.class); - ConfigGui.init(ModInfo.ID, Config.Client.Graphics.class); - ConfigGui.init(ModInfo.ID, Config.Client.Graphics.Quality.class); - ConfigGui.init(ModInfo.ID, Config.Client.Graphics.FogQuality.class); - ConfigGui.init(ModInfo.ID, Config.Client.Graphics.AdvancedGraphics.class); - ConfigGui.init(ModInfo.ID, Config.Client.WorldGenerator.class); - ConfigGui.init(ModInfo.ID, Config.Client.Advanced.class); - ConfigGui.init(ModInfo.ID, Config.Client.Advanced.Threading.class); - ConfigGui.init(ModInfo.ID, Config.Client.Advanced.Debugging.class); - ConfigGui.init(ModInfo.ID, Config.Client.Advanced.Buffers.class); - } -} diff --git a/src/main/java/com/seibel/lod/core/ModInfo.java b/src/main/java/com/seibel/lod/core/ModInfo.java deleted file mode 100644 index 5a7e25f8b..000000000 --- a/src/main/java/com/seibel/lod/core/ModInfo.java +++ /dev/null @@ -1,42 +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.core; - -/** - * This file is similar to mcmod.info - *
- * If you are looking at this mod's source code and don't - * know where to start. - * Go to the api/lod package (folder) and take a look at the ClientApi.java file, - * Pretty much all of the mod stems from there. - * - * @author James Seibel - * @version 11-29-2021 - */ -public final class ModInfo -{ - public static final String ID = "lod"; - /** The internal mod name */ - public static final String NAME = "DistantHorizons"; - /** Human readable version of NAME */ - public static final String READABLE_NAME = "Distant Horizons"; - public static final String API = "LodAPI"; - public static final String VERSION = "a1.5.4"; -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/api/ApiShared.java b/src/main/java/com/seibel/lod/core/api/ApiShared.java deleted file mode 100644 index 36f8635b5..000000000 --- a/src/main/java/com/seibel/lod/core/api/ApiShared.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.core.api; - -import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory; -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.objects.lod.LodWorld; - -/** - * This stores objects and variables that - * are shared between the different Core api classes. - * - * @author James Seibel - * @version 11-12-2021 - */ -public class ApiShared -{ - public ApiShared INSTANCE = new ApiShared(); - - public static final LodBufferBuilderFactory lodBufferBuilderFactory = new LodBufferBuilderFactory(); - public static final LodWorld lodWorld = new LodWorld(); - public static final LodBuilder lodBuilder = new LodBuilder(); - - /** Used to determine if the LODs should be regenerated */ - public static int previousChunkRenderDistance = 0; - /** Used to determine if the LODs should be regenerated */ - public static int previousLodRenderDistance = 0; - - - - private ApiShared() - { - - } - -} diff --git a/src/main/java/com/seibel/lod/core/api/ClientApi.java b/src/main/java/com/seibel/lod/core/api/ClientApi.java deleted file mode 100644 index 439e74ef0..000000000 --- a/src/main/java/com/seibel/lod/core/api/ClientApi.java +++ /dev/null @@ -1,190 +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.core.api; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.seibel.lod.core.ModInfo; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.objects.math.Mat4f; -import com.seibel.lod.core.render.GLProxy; -import com.seibel.lod.core.render.LodRenderer; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.SingletonHandler; -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.core.wrapperInterfaces.minecraft.IProfilerWrapper; - -/** - * This holds the methods that should be called - * by the host mod loader (Fabric, Forge, etc.). - * Specifically for the client. - * - * @author James Seibel - * @version 11-12-2021 - */ -public class ClientApi -{ - public static final ClientApi INSTANCE = new ClientApi(); - public static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME); - - public static LodRenderer renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory); - - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final EventApi EVENT_API = EventApi.INSTANCE; - - /** - * there is some setup that should only happen once, - * once this is true that setup has completed - */ - private boolean firstTimeSetupComplete = false; - private boolean configOverrideReminderPrinted = false; - - - - private ClientApi() - { - - } - - - - - public void renderLods(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, 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.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded()) - return; - - LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension()); - if (lodDim == null) - return; - - DetailDistanceUtil.updateSettings(); - EVENT_API.viewDistanceChangedEvent(); - EVENT_API.playerMoveEvent(lodDim); - - lodDim.cutRegionNodesAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ()); - lodDim.expandOrLoadRegionsAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ()); - - - - if (CONFIG.client().advanced().debugging().getDrawLods()) - { - // 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 - IProfilerWrapper profiler = MC.getProfiler(); - profiler.pop(); // get out of "terrain" - profiler.push("LOD"); - - - ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, 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 - ApiShared.previousChunkRenderDistance = MC_RENDER.getRenderDistance(); - ApiShared.previousLodRenderDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance(); - } - catch (Exception e) - { - ClientApi.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.sendChatMessage(ModInfo.READABLE_NAME + " experimental build " + ModInfo.VERSION); - MC.sendChatMessage("You are running a unsupported version of the mod!"); - MC.sendChatMessage("Here be dragons!"); - - configOverrideReminderPrinted = true; - } - -// CONFIG.client().worldGenerator().setDistanceGenerationMode(DistanceGenerationMode.FULL); - -// CONFIG.client().worldGenerator().setGenerationPriority(GenerationPriority.AUTO); - -// CONFIG.client().graphics().advancedGraphics().setGpuUploadMethod(GpuUploadMethod.BUFFER_STORAGE); -// CONFIG.client().graphics().quality().setLodChunkRenderDistance(128); - -// CONFIG.client().graphics().fogQuality().setFogDrawMode(FogDrawMode.FOG_ENABLED); -// CONFIG.client().graphics().fogQuality().setFogDistance(FogDistance.FAR); -// CONFIG.client().graphics().fogQuality().setDisableVanillaFog(true); - -// CONFIG.client().advanced().buffers().setRebuildTimes(BufferRebuildTimes.FREQUENT); - - - CONFIG.client().advanced().debugging().setDebugKeybindingsEnabled(true); - } - - - - - //=================// - // Lod maintenance // - //=================// - - /** 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; - } - - - - - - - - - -} diff --git a/src/main/java/com/seibel/lod/core/api/EventApi.java b/src/main/java/com/seibel/lod/core/api/EventApi.java deleted file mode 100644 index 580319631..000000000 --- a/src/main/java/com/seibel/lod/core/api/EventApi.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.core.api; - -import org.lwjgl.glfw.GLFW; - -import com.seibel.lod.core.builders.worldGeneration.LodGenWorker; -import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator; -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.objects.lod.RegionPos; -import com.seibel.lod.core.render.LodRenderer; -import com.seibel.lod.core.util.DataPointUtil; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.util.ThreadMapUtil; -import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - -/** - * This holds the methods that should be called - * by the host mod loader (Fabric, Forge, etc.). - * Specifically server and client events. - * - * @author James Seibel - * @version 11-12-2021 - */ -public class EventApi -{ - public static final EventApi INSTANCE = new EventApi(); - - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - - /** - * can be set if we want to recalculate variables related - * to the LOD view distance - */ - private boolean recalculateWidths = false; - - - private EventApi() - { - - } - - - - - //=============// - // tick events // - //=============// - - public void serverTickEvent() - { - if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded()) - return; - - LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension()); - if (lodDim == null) - return; - - LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ClientApi.renderer, ApiShared.lodBuilder); - } - - - - - //==============// - // world events // - //==============// - - public void chunkLoadEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType) - { - ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld, dimType, DistanceGenerationMode.FULL); - } - - public void worldSaveEvent() - { - ApiShared.lodWorld.saveAllDimensions(); - } - - /** This is also called when a new dimension loads */ - public void worldLoadEvent(IWorldWrapper world) - { - DataPointUtil.WORLD_HEIGHT = world.getHeight(); - //LodNodeGenWorker.restartExecutorService(); - //ThreadMapUtil.clearMaps(); - - // the player just loaded a new world/dimension - ApiShared.lodWorld.selectWorld(LodUtil.getWorldID(world)); - - // make sure the correct LODs are being rendered - // (if this isn't done the previous world's LODs may be drawn) - ClientApi.renderer.regenerateLODsNextFrame(); - } - - /** This is also called when the user disconnects from a server+ */ - public void worldUnloadEvent() - { - // the player just unloaded a world/dimension - ThreadMapUtil.clearMaps(); - - new Thread(() -> checkIfDisconnectedFromServer()).start(); - } - private void checkIfDisconnectedFromServer() - { - try - { - // world unloading events are called before disconnecting from the server, - // so we need to wait a second for MC to disconnect - Thread.sleep(1000); - } - catch (InterruptedException e) - { - // this should never happen, but just in case - e.printStackTrace(); - } - - - if (MC.getWrappedClientWorld() == null || (!MC.connectedToServer() && !MC.hasSinglePlayerServer())) - { - // the player just left the server - - // TODO should "resetMod()" be called here? -James - - // if this isn't done unfinished tasks may be left in the queue - // preventing new LodChunks form being generated - LodGenWorker.restartExecutorService(); - - LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0); - ApiShared.lodWorld.deselectWorld(); - - - // prevent issues related to the buffer builder - // breaking or retaining previous data when changing worlds. - ClientApi.renderer.destroyBuffers(); - recalculateWidths = true; - ClientApi.renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory); - - - // make sure the nulled objects are freed. - // (this prevents an out of memory error when - // changing worlds) - System.gc(); - } - } - - public void blockChangeEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType) - { - // recreate the LOD where the blocks were changed - ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld, dimType); - } - - - - - //=============// - // Misc Events // - //=============// - - public void onKeyInput(int key, int keyAction) - { - if (CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled()) - { - if (key == GLFW.GLFW_KEY_F4 && keyAction == GLFW.GLFW_PRESS) - { - CONFIG.client().advanced().debugging().setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext()); - } - - if (key == GLFW.GLFW_KEY_F6 && keyAction == GLFW.GLFW_PRESS) - { - CONFIG.client().advanced().debugging().setDrawLods(!CONFIG.client().advanced().debugging().getDrawLods()); - } - } - } - - /** Re-centers the given LodDimension if it needs to be. */ - public 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) - { - ApiShared.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. */ - public void viewDistanceChangedEvent() - { - // calculate how wide the dimension(s) should be in regions - int chunksWide; - if (MC.getWrappedClientWorld().getDimensionType().hasCeiling()) - chunksWide = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1; - else - chunksWide = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 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 (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths) - { - ApiShared.lodWorld.saveAllDimensions(); - - // update the dimensions to fit the new width - ApiShared.lodWorld.resizeDimensionRegionWidth(newWidth); - ApiShared.lodBuilder.defaultDimensionWidthInRegions = newWidth; - ClientApi.renderer.setupBuffers(ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension())); - - recalculateWidths = false; - //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); - } - DetailDistanceUtil.updateSettings(); - } - - -} diff --git a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java deleted file mode 100644 index ab792b48c..000000000 --- a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/LodBufferBuilderFactory.java +++ /dev/null @@ -1,1062 +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.core.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 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.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.enums.config.GpuUploadMethod; -import com.seibel.lod.core.enums.config.VanillaOverdraw; -import com.seibel.lod.core.enums.rendering.GLProxyContext; -import com.seibel.lod.core.objects.PosToRenderContainer; -import com.seibel.lod.core.objects.VertexOptimizer; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.objects.lod.LodRegion; -import com.seibel.lod.core.objects.lod.RegionPos; -import com.seibel.lod.core.objects.opengl.LodBufferBuilder; -import com.seibel.lod.core.objects.opengl.LodVertexBuffer; -import com.seibel.lod.core.render.GLProxy; -import com.seibel.lod.core.render.LodRenderer; -import com.seibel.lod.core.util.DataPointUtil; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodThreadFactory; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.util.ThreadMapUtil; -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.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; - -/** - * This object creates the buffers that are - * rendered by the LodRenderer. - * - * @author James Seibel - * @version 11-29-2021 - */ -public class LodBufferBuilderFactory -{ - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class); - - /** The thread used to generate new LODs off the main thread. */ - public static final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilderFactory.class.getSimpleName() + " - main")); - /** The threads used to generate buffers. */ - public static final ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads(), 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 LodBufferBuilder[][][] 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 LodVertexBuffer[][][] buildableVbos; - /** VBOs that are sent over to the LodNodeRenderer */ - public volatile LodVertexBuffer[][][] 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 VertexOptimizer[][] vertexOptimizerCache; - 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 AbstractChunkPosWrapper drawableCenterChunkPos = WRAPPER_FACTORY.createChunkPos(); - private volatile AbstractChunkPosWrapper buildableCenterChunkPos = WRAPPER_FACTORY.createChunkPos(); - - - - - - - public LodBufferBuilderFactory() - { - - } - - /** - * 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, - AbstractBlockPosWrapper 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; - - if (MC.getCurrentLightMap() == null) - // the lighting hasn't loaded 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, - AbstractBlockPosWrapper playerBlockPos, boolean fullRegen) - { - bufferLock.lock(); - - try - { - // round the player's block position down to the nearest chunk BlockPos - AbstractChunkPosWrapper playerChunkPos = WRAPPER_FACTORY.createChunkPos(playerBlockPos); - AbstractBlockPosWrapper 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 (vertexOptimizerCache == null) - vertexOptimizerCache = new VertexOptimizer[lodDim.getWidth()][lodDim.getWidth()]; - - if (vertexOptimizerCache.length != lodDim.getWidth()) - vertexOptimizerCache = new VertexOptimizer[lodDim.getWidth()][lodDim.getWidth()]; - - // this will be the center of the VBOs once they have been built - buildableCenterChunkPos = playerChunkPos; - - - //================================// - // create the nodeToRenderThreads // - //================================// - - skyLightPlayer = MC.getWrappedClientWorld().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 - LodBufferBuilder[] 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()) - { - ClientApi.LOGGER.info("Buffer building quit early"); - 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; - VertexOptimizer vertexOptimizer = 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 (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS) - { - - xAdj = posX + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).x; - zAdj = posZ + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).z; - 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[VertexOptimizer.DIRECTION_INDEX.get(lodDirection)] = false; - adjData.get(lodDirection)[verticalIndex] = data; - } - } - else - { - //Otherwise, we check if this position is - data = lodDim.getSingleData(detailLevel, xAdj, zAdj); - - adjData.get(lodDirection)[0] = DataPointUtil.EMPTY_DATA; - - if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk)) - && !DataPointUtil.isVoid(data)) - { - adjShadeDisabled[VertexOptimizer.DIRECTION_INDEX.get(lodDirection)] = 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(LodDirection.UP)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex - 1); - else - adjData.get(LodDirection.UP)[0] = DataPointUtil.EMPTY_DATA; - - - //we get the below block as adj DOWN - if (verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ) - 1) - adjData.get(LodDirection.DOWN)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex + 1); - else - adjData.get(LodDirection.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 - CONFIG.client().graphics().advancedGraphics().getLodTemplate().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, data, adjData, - detailLevel, posX, posZ, vertexOptimizer, 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()) - { - ClientApi.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) - { - ClientApi.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: "); - e.printStackTrace(); - } - finally - { - try - { - // clean up any potentially open resources - if (buildableBuffers != null) - closeBuffers(fullRegen, lodDim); - - // upload the new buffers - uploadBuffers(fullRegen, lodDim); - } - catch (Exception e) - { - ClientApi.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, AbstractChunkPosWrapper 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 (CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw() == 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) - { - try - { - bufferLock.lock(); - - int numbRegionsWide = lodDimension.getWidth(); - long regionMemoryRequired; - int numberOfBuffers; - - GLProxy glProxy = GLProxy.getInstance(); - GLProxyContext oldContext = glProxy.getGlContext(); - glProxy.setGlContext(GLProxyContext.LOD_BUILDER); - - - previousRegionWidth = numbRegionsWide; - numberOfBuffersPerRegion = new int[numbRegionsWide][numbRegionsWide]; - buildableBuffers = new LodBufferBuilder[numbRegionsWide][numbRegionsWide][]; - - buildableVbos = new LodVertexBuffer[numbRegionsWide][numbRegionsWide][]; - drawableVbos = new LodVertexBuffer[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 LodBufferBuilder[numberOfBuffers]; - buildableVbos[x][z] = new LodVertexBuffer[numberOfBuffers]; - drawableVbos[x][z] = new LodVertexBuffer[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 LodBufferBuilder[1]; - buildableVbos[x][z] = new LodVertexBuffer[1]; - drawableVbos[x][z] = new LodVertexBuffer[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 LodBufferBuilder((int) regionMemoryRequired); - - buildableVbos[x][z][i] = new LodVertexBuffer(); - drawableVbos[x][z][i] = new LodVertexBuffer(); - - - // 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_STATIC_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_STATIC_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); - } - } - } - } - - glProxy.setGlContext(oldContext); - } - catch (Exception e) - { - ClientApi.LOGGER.info("setupBuffers ran into trouble: " + e.getMessage(), e); - } - finally - { - // this shouldn't normally happen, but just in case it sill prevent deadlock - 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() - { - try - { - 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]; - - // make sure the buffers are deleted in a openGL context - GLProxy.getInstance().recordOpenGlCall(() -> - { - 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; - - - GLProxy.getInstance().recordOpenGlCall(() -> - { - 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; - } - catch (Exception e) - { - ClientApi.LOGGER.info("destroyBuffers ran into trouble: " + e.getMessage(), e); - } - finally - { - // this shouldn't normally happen, but just in case it sill prevent deadlock - 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(); - long fence = 0; - - 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 = CONFIG.client().advanced().buffers().getGpuUploadMethod(); - if (!glProxy.bufferStorageSupported && uploadMethod == GpuUploadMethod.BUFFER_STORAGE) - { - // if buffer storage isn't supported - // default to SUB_DATA - CONFIG.client().advanced().buffers().setGpuUploadMethod(GpuUploadMethod.SUB_DATA); - uploadMethod = GpuUploadMethod.SUB_DATA; - } - - // determine the upload timeout - int uploadTimeoutInMS = CONFIG.client().advanced().buffers().getGpuUploadTimeoutInMilliseconds(); - - // James has no idea if this does anything helpful, - // but in theory it should prevent OpenGL from drawing and - // writing to a buffer at the same time. - GL45.glMemoryBarrier(GL45.GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); - fence = GL45.glFenceSync(GL45.GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - - - // 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].getCleanedByteBuffer(); - vboUpload(x,z,i, uploadBuffer, true, uploadMethod); - lodDim.setRegenRegionBufferByArrayIndex(x, z, false); - - - // upload buffers over an extended period of time - // to hopefully prevent stuttering. - if (uploadTimeoutInMS != 0) - Thread.sleep(uploadTimeoutInMS); - GL15.glFinish(); - } - } - } - } - - // make sure all of the uploads finish before continuing - GL45.glClientWaitSync(fence, GL45.GL_SYNC_FLUSH_COMMANDS_BIT, 5L * 1000000000); // wait up to 5 seconds - } - catch (Exception e) - { - // this doesn't appear to be necessary anymore, but just in case. - ClientApi.LOGGER.error(LodBufferBuilderFactory.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage()); - e.printStackTrace(); - } - finally - { - GL15.glFinish(); - if (fence != 0) - GL45.glDeleteSync(fence); - - // 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(int xIndex, int zIndex, int iIndex, ByteBuffer uploadBuffer, - boolean allowBufferExpansion, GpuUploadMethod uploadMethod) - { - // get the vbos, buffers, ids, etc. - int storageBufferId = 0; - if (buildableStorageBufferIds != null) - storageBufferId = buildableStorageBufferIds[xIndex][zIndex][iIndex]; - - LodVertexBuffer vbo = buildableVbos[xIndex][zIndex][iIndex]; - - - - - // 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() / ((Float.BYTES * 3) + (Byte.BYTES * 4))); // TODO make this change with the LodTemplate - - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); - try - { - // if possible use the faster buffer storage route - if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE && storageBufferId != 0) - { - // 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(xIndex,zIndex,iIndex, 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 | GL30.GL_MAP_INVALIDATE_BUFFER_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_STATIC_DRAW); - GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); - } - else - { - vboBuffer.put(uploadBuffer); - } - } - else if (uploadMethod == GpuUploadMethod.DATA) - { - // hybrid bufferData // - // high stutter, low GPU usage - // But simplest/most compatible - - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer.capacity(), GL15.GL_STATIC_DRAW); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer, GL15.GL_STATIC_DRAW); - } - else - { - // hybrid subData/bufferData // - // less stutter, low GPU usage - - 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_STATIC_DRAW); - } - GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); - } - } - catch (Exception e) - { - ClientApi.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()) - { - try - { - LodVertexBuffer[][][] tmpVbo = drawableVbos; - drawableVbos = buildableVbos; - buildableVbos = tmpVbo; - - int[][][] tmpStorage = drawableStorageBufferIds; - drawableStorageBufferIds = buildableStorageBufferIds; - buildableStorageBufferIds = tmpStorage; - - drawableCenterChunkPos = buildableCenterChunkPos; - - // the vbos have been swapped - switchVbos = false; - } - catch (Exception e) - { - // this shouldn't normally happen, but just in case it sill prevent deadlock - ClientApi.LOGGER.info("getVertexBuffers ran into trouble: " + e.getMessage(), e); - } - finally - { - 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 LodVertexBuffer[][][] vbos; - public final int[][][] storageBufferIds; - public final AbstractChunkPosWrapper drawableCenterChunkPos; - - public VertexBuffersAndOffset(LodVertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, AbstractChunkPosWrapper 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/core/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java deleted file mode 100644 index 69bb57b10..000000000 --- a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java +++ /dev/null @@ -1,52 +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.core.builders.bufferBuilding.lodTemplates; - -import java.util.Map; - -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.enums.rendering.DebugMode; -import com.seibel.lod.core.objects.VertexOptimizer; -import com.seibel.lod.core.objects.opengl.LodBufferBuilder; -import com.seibel.lod.core.util.ColorUtil; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; - -/** - * This is the abstract class used to create different - * BufferBuilders. - * @author James Seibel - * @version 11-13-2021 - */ -public abstract class AbstractLodTemplate -{ - /** Uploads the given LOD to the buffer. */ - public abstract void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map adjData, - byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled); - - /** add the given position and color to the buffer */ - protected void addPosAndColor(LodBufferBuilder buffer, - float x, float y, float 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/core/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java deleted file mode 100644 index aaf016b05..000000000 --- a/src/main/java/com/seibel/lod/core/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.core.builders.bufferBuilding.lodTemplates; - -import java.util.Map; - -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.enums.rendering.DebugMode; -import com.seibel.lod.core.objects.VertexOptimizer; -import com.seibel.lod.core.objects.opengl.LodBufferBuilder; -import com.seibel.lod.core.util.ColorUtil; -import com.seibel.lod.core.util.DataPointUtil; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; - -/** - * Builds LODs as rectangular prisms. - * @author James Seibel - * @version 11-8-2021 - */ -public class CubicLodTemplate extends AbstractLodTemplate -{ - - public CubicLodTemplate() - { - - } - - @Override - public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map adjData, - byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled) - { - if (vertexOptimizer == 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( - vertexOptimizer, - 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, vertexOptimizer); - } - - private void generateBoundingBox(VertexOptimizer vertexOptimizer, - int height, int depth, int width, - double xOffset, double yOffset, double zOffset, - AbstractBlockPosWrapper 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(); - vertexOptimizer.reset(); - vertexOptimizer.setColor(color, adjShadeDisabled); - vertexOptimizer.setLights(skyLight, blockLight); - vertexOptimizer.setWidth(width, height - depth, width); - vertexOptimizer.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z)); - vertexOptimizer.setUpCulling(32, bufferCenterBlockPos); - vertexOptimizer.setAdjData(adjData); - } - - private void addBoundingBoxToBuffer(LodBufferBuilder buffer, VertexOptimizer vertexOptimizer) - { - int color; - int skyLight; - int blockLight; - for (LodDirection lodDirection : VertexOptimizer.DIRECTIONS) - { - if(vertexOptimizer.isCulled(lodDirection)) - continue; - - int verticalFaceIndex = 0; - while (vertexOptimizer.shouldRenderFace(lodDirection, verticalFaceIndex)) - { - for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++) - { - color = vertexOptimizer.getColor(lodDirection); - skyLight = vertexOptimizer.getSkyLight(lodDirection, verticalFaceIndex); - blockLight = vertexOptimizer.getBlockLight(); - color = ColorUtil.applyLightValue(color, skyLight, blockLight); - addPosAndColor(buffer, - vertexOptimizer.getX(lodDirection, vertexIndex), - vertexOptimizer.getY(lodDirection, vertexIndex, verticalFaceIndex) + DataPointUtil.VERTICAL_OFFSET, - vertexOptimizer.getZ(lodDirection, vertexIndex), - color); - } - verticalFaceIndex++; - } - } - } - -} diff --git a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java deleted file mode 100644 index a1c302b00..000000000 --- a/src/main/java/com/seibel/lod/core/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.core.builders.bufferBuilding.lodTemplates; - -import java.util.Map; - -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.enums.rendering.DebugMode; -import com.seibel.lod.core.objects.VertexOptimizer; -import com.seibel.lod.core.objects.opengl.LodBufferBuilder; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; - -/** - * 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(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map adjData, - byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled) - { - ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!"); - } - -} diff --git a/src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java b/src/main/java/com/seibel/lod/core/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java deleted file mode 100644 index 489869645..000000000 --- a/src/main/java/com/seibel/lod/core/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.core.builders.bufferBuilding.lodTemplates; - -import java.util.Map; - -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.enums.rendering.DebugMode; -import com.seibel.lod.core.objects.VertexOptimizer; -import com.seibel.lod.core.objects.opengl.LodBufferBuilder; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; - -/** - * 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(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map adjData, - byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled) - { - ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!"); - } - -} diff --git a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java b/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java deleted file mode 100644 index 6351fdd1e..000000000 --- a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java +++ /dev/null @@ -1,541 +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.core.builders.lodBuilding; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.enums.config.HorizontalResolution; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.objects.lod.LodRegion; -import com.seibel.lod.core.objects.lod.LodWorld; -import com.seibel.lod.core.util.ColorUtil; -import com.seibel.lod.core.util.DataPointUtil; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodThreadFactory; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.util.ThreadMapUtil; -import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper; -import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper; -import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - -/** - * This object is in charge of creating Lod related objects. - * - * @author Cola - * @author Leonardo Amato - * @author James Seibel - * @version 10-22-2021 - */ -@SuppressWarnings("GrazieInspection") public class LodBuilder -{ - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final IBlockColorSingletonWrapper BLOCK_COLOR = SingletonHandler.get(IBlockColorSingletonWrapper.class); - private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class); - - /** 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; - - - private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); - private final ILodConfigWrapperSingleton config = SingletonHandler.get(ILodConfigWrapperSingleton.class); - - - - /** - * 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(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim) - { - generateLodNodeAsync(chunk, lodWorld, dim, DistanceGenerationMode.FULL); - } - - public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim, 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(() -> - { - //noinspection GrazieInspection - try - { - // we need a loaded client world in order to - // get the textures for blocks - if (MC.getWrappedClientWorld() == 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.hasSinglePlayerServer() && !MC.connectedToServer()) - return; - - // 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, IChunkWrapper 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, IChunkWrapper chunk, LodBuilderConfig config) - throws IllegalArgumentException - { - //long executeTime = System.currentTimeMillis(); - 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 (MC.getWrappedClientWorld() == 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.WORLD_HEIGHT / 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()); - //executeTime = System.currentTimeMillis() - executeTime; - //if (executeTime > 0) ClientApi.LOGGER.info("generateLodNodeFromChunk level: " + detailLevel + " time ms: " + executeTime); - } - - /** creates a vertical DataPoint */ - private long[] createVerticalDataToMerge(HorizontalResolution detail, IChunkWrapper 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.WORLD_HEIGHT / 2 + 1; - - AbstractChunkPosWrapper 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.getWrappedClientWorld().getDimensionType().hasCeiling(); - boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight(); - boolean isDefault; - AbstractBlockPosWrapper blockPos = FACTORY.createBlockPos(); - 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.WORLD_HEIGHT - DataPointUtil.VERTICAL_OFFSET + 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 - DataPointUtil.VERTICAL_OFFSET, depth - DataPointUtil.VERTICAL_OFFSET, 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(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper 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(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper 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(IChunkWrapper chunk, LodBuilderConfig builderConfig, int xRel, int yAbs, int zRel, AbstractBlockPosWrapper blockPos) - { - int colorInt; - if (builderConfig.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 (config.client().worldGenerator().getBlocksToAvoid().nonFull || config.client().worldGenerator().getBlocksToAvoid().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(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock) - { - int skyLight = 0; - int blockLight; - // 1 means the lighting is a guess - int isDefault = 0; - - IWorldWrapper world = MC.getWrappedServerWorld(); - - 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) - { - // 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.getWrappedClientWorld().getSeaLevel() - 5) - { - skyLight = 12; - isDefault = 1; - } - else - skyLight = 0; - } - } - else - { - world = MC.getWrappedClientWorld(); - if (world==null) - { - blockLight = 0; - skyLight = 12; - isDefault = 1; - } - else - { - // 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.getWrappedClientWorld().getSeaLevel() - 5) - { - skyLight = 12; - isDefault = 1; - } - else - 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(IChunkWrapper chunk, AbstractBlockPosWrapper 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(); - - IBlockColorWrapper blockColorWrapper; - IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos); - - if (chunk.isWaterLogged(blockPos)) - blockColorWrapper = BLOCK_COLOR.getWaterColor(); - else - blockColorWrapper = chunk.getBlockColorWrapper(blockPos); - - if (blockShapeWrapper.isToAvoid()) - return 0; - - colorOfBlock = blockColorWrapper.getColor(); - - - if (blockColorWrapper.hasTint()) - { - IBiomeWrapper 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(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos) - { - if (chunk.isWaterLogged(blockPos)) - return true; - - boolean nonFullAvoidance = config.client().worldGenerator().getBlocksToAvoid().nonFull; - boolean noCollisionAvoidance = config.client().worldGenerator().getBlocksToAvoid().noCollision; - - IBlockShapeWrapper block = chunk.getBlockShapeWrapper(blockPos); - return !block.isToAvoid() - && !(nonFullAvoidance && block.isNonFull()) - && !(noCollisionAvoidance && block.hasNoCollision()); - - } -} diff --git a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilderConfig.java b/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilderConfig.java deleted file mode 100644 index e88a5995b..000000000 --- a/src/main/java/com/seibel/lod/core/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.core.builders.lodBuilding; - -import com.seibel.lod.core.enums.config.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.FULL; - } - - /** - * @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/core/builders/worldGeneration/LodGenWorker.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodGenWorker.java deleted file mode 100644 index 77eb213b2..000000000 --- a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodGenWorker.java +++ /dev/null @@ -1,212 +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.core.builders.worldGeneration; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -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.IWrapperFactory; -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; - -/** - * This is used to generate a LodChunk at a given ChunkPos. - * - * @author James Seibel - * @version 11-20-2021 - */ -public class LodGenWorker -{ - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class); - - public static ExecutorService genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); - - private final LodChunkGenThread thread; - - - - public LodGenWorker(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode, - LodBuilder newLodBuilder, - LodDimension newLodDimension, IWorldWrapper serverWorld) - { - // 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 (serverWorld == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld"); - - - - thread = new LodChunkGenThread(newPos, newGenerationMode, - newLodBuilder, - newLodDimension, serverWorld); - } - - public void queueWork() - { - if (CONFIG.client().worldGenerator().getDistanceGenerationMode() == DistanceGenerationMode.FULL) - { - // if we are using FULL generation there is no reason - // to queue up a bunch of generation requests, - // because MC's internal server (as of 1.16.5) only - // responds with a single thread. And we don't - // want to cause more lag than necessary or queue up - // requests that may end up being unneeded. - thread.run(); - } - else - { - // Every other method can - // be done asynchronously - genThreads.execute(thread); - } - - // useful for debugging -// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods()); -// ClientProxy.LOGGER.info(genThreads.toString()); - } - - - - - private static class LodChunkGenThread implements Runnable - { - private final AbstractWorldGeneratorWrapper worldGenWrapper; - - public final LodDimension lodDim; - public final DistanceGenerationMode generationMode; - - private final AbstractChunkPosWrapper pos; - - public LodChunkGenThread(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode, - LodBuilder newLodBuilder, - LodDimension newLodDimension, IWorldWrapper worldWrapper) - { - worldGenWrapper = FACTORY.createWorldGenerator(newLodBuilder, newLodDimension, worldWrapper); - - pos = newPos; - generationMode = newGenerationMode; - lodDim = newLodDimension; - } - - @Override - public void run() - { - try - { - // only generate LodChunks if they can - // be added to the current LodDimension - - if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS)) - { - switch (generationMode) - { - case NONE: - // don't generate - break; - case BIOME_ONLY: - case BIOME_ONLY_SIMULATE_HEIGHT: - // fastest - worldGenWrapper.generateBiomesOnly(pos, generationMode); - break; - case SURFACE: - // faster - worldGenWrapper.generateSurface(pos); - break; - case FEATURES: - // fast - worldGenWrapper.generateFeatures(pos); - break; - case FULL: - // very slow - worldGenWrapper.generateFull(pos); - break; - } - - -// 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) - { - ClientApi.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 - - - } - - - /** - * 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(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); - } - -} diff --git a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java deleted file mode 100644 index e97498c7c..000000000 --- a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java +++ /dev/null @@ -1,209 +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.core.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.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.objects.PosToGenerateContainer; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.render.LodRenderer; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodThreadFactory; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - -/** - * A singleton that handles all long distance LOD world generation. - * @author Leonardo Amato - * @author James Seibel - * @version 9-25-2021 - */ -public class LodWorldGenerator -{ - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class); - - - /** This holds the thread used to create LOD generation requests 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 (CONFIG.client().worldGenerator().getDistanceGenerationMode() != 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 = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() * 8; - - Thread generatorThread = new Thread(() -> - { - try - { - // round the player's block position down to the nearest chunk BlockPos - int playerPosX = MC.getPlayerBlockPos().getX(); - int playerPosZ = MC.getPlayerBlockPos().getZ(); - - - //=======================================// - // fill in positionsWaitingToBeGenerated // - //=======================================// - - IWorldWrapper 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++; - - AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(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); - genWorker.queueWork(); - } - - - // 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++; - - AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(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); - genWorker.queueWork(); - } - } - - } - 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/core/dataFormat/BlockDataFormat.java b/src/main/java/com/seibel/lod/core/dataFormat/BlockDataFormat.java deleted file mode 100644 index d8becb6f5..000000000 --- a/src/main/java/com/seibel/lod/core/dataFormat/BlockDataFormat.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.seibel.lod.core.dataFormat; - -public class BlockDataFormat -{ -} diff --git a/src/main/java/com/seibel/lod/core/dataFormat/ColorFormat.java b/src/main/java/com/seibel/lod/core/dataFormat/ColorFormat.java deleted file mode 100644 index 750fcde64..000000000 --- a/src/main/java/com/seibel/lod/core/dataFormat/ColorFormat.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.seibel.lod.core.dataFormat; - -public class ColorFormat -{ -} diff --git a/src/main/java/com/seibel/lod/core/dataFormat/DepthHeightFormat.java b/src/main/java/com/seibel/lod/core/dataFormat/DepthHeightFormat.java deleted file mode 100644 index 3573abec2..000000000 --- a/src/main/java/com/seibel/lod/core/dataFormat/DepthHeightFormat.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.seibel.lod.core.dataFormat; - -public class DepthHeightFormat -{ -} diff --git a/src/main/java/com/seibel/lod/core/dataFormat/LightFormat.java b/src/main/java/com/seibel/lod/core/dataFormat/LightFormat.java deleted file mode 100644 index 188a76cdb..000000000 --- a/src/main/java/com/seibel/lod/core/dataFormat/LightFormat.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.seibel.lod.core.dataFormat; - -public class LightFormat -{ -} diff --git a/src/main/java/com/seibel/lod/core/dataFormat/PositionDataFormat.java b/src/main/java/com/seibel/lod/core/dataFormat/PositionDataFormat.java deleted file mode 100644 index 8f8940ebe..000000000 --- a/src/main/java/com/seibel/lod/core/dataFormat/PositionDataFormat.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.seibel.lod.core.dataFormat; - -public class PositionDataFormat -{ -} diff --git a/src/main/java/com/seibel/lod/core/enums/LodDirection.java b/src/main/java/com/seibel/lod/core/enums/LodDirection.java deleted file mode 100644 index 2467ed14b..000000000 --- a/src/main/java/com/seibel/lod/core/enums/LodDirection.java +++ /dev/null @@ -1,529 +0,0 @@ -package com.seibel.lod.core.enums; - -import java.util.Arrays; -import java.util.Locale; -import java.util.Map; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import com.seibel.lod.core.objects.math.Vec3i; - -/** - * A (almost) exact copy of Minecraft's - * Direction enum. - * - * @author James Seibel - * @version 11-13-2021 - */ -public enum LodDirection -{ - DOWN(0, 1, -1, "down", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.Y, new Vec3i(0, -1, 0)), - UP(1, 0, -1, "up", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.Y, new Vec3i(0, 1, 0)), - NORTH(2, 3, 2, "north", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.Z, new Vec3i(0, 0, -1)), - SOUTH(3, 2, 0, "south", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.Z, new Vec3i(0, 0, 1)), - WEST(4, 5, 1, "west", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.X, new Vec3i(-1, 0, 0)), - EAST(5, 4, 3, "east", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.X, new Vec3i(1, 0, 0)); - -// private final int data3d; -// private final int oppositeIndex; -// private final int data2d; - - private final String name; - private final LodDirection.Axis axis; - private final LodDirection.AxisDirection axisDirection; - private final Vec3i normal; - private static final LodDirection[] VALUES = values(); - - private static final Map BY_NAME = Arrays.stream(VALUES).collect(Collectors.toMap(LodDirection::getName, (p_199787_0_) -> - { - return p_199787_0_; - })); - -// private static final LodDirection[] BY_3D_DATA = Arrays.stream(VALUES).sorted(Comparator.comparingInt((p_199790_0_) -> -// { -// return p_199790_0_.data3d; -// })).toArray((p_199788_0_) -> -// { -// return new LodDirection[p_199788_0_]; -// }); -// -// private static final LodDirection[] BY_2D_DATA = Arrays.stream(VALUES).filter((p_199786_0_) -> -// { -// return p_199786_0_.getAxis().isHorizontal(); -// }).sorted(Comparator.comparingInt((p_199789_0_) -> -// { -// return p_199789_0_.data2d; -// })).toArray((p_199791_0_) -> -// { -// return new LodDirection[p_199791_0_]; -// }); - -// private static final Long2ObjectMap BY_NORMAL = Arrays.stream(VALUES).collect(Collectors.toMap((p_218385_0_) -> -// { -// return (new BlockPos(p_218385_0_.getNormal())).asLong(); -// }, (p_218384_0_) -> -// { -// return p_218384_0_; -// }, (p_218386_0_, p_218386_1_) -> -// { -// throw new IllegalArgumentException("Duplicate keys"); -// }, Long2ObjectOpenHashMap::new)); - - - - LodDirection(int p_i46016_3_, int p_i46016_4_, int p_i46016_5_, String p_i46016_6_, LodDirection.AxisDirection p_i46016_7_, LodDirection.Axis p_i46016_8_, Vec3i p_i46016_9_) - { -// this.data3d = p_i46016_3_; -// this.data2d = p_i46016_5_; -// this.oppositeIndex = p_i46016_4_; - this.name = p_i46016_6_; - this.axis = p_i46016_8_; - this.axisDirection = p_i46016_7_; - this.normal = p_i46016_9_; - } - - - - -// public static LodDirection[] orderedByNearest(Entity p_196054_0_) -// { -// float f = p_196054_0_.getViewXRot(1.0F) * ((float) Math.PI / 180F); -// float f1 = -p_196054_0_.getViewYRot(1.0F) * ((float) Math.PI / 180F); -// float f2 = MathHelper.sin(f); -// float f3 = MathHelper.cos(f); -// float f4 = MathHelper.sin(f1); -// float f5 = MathHelper.cos(f1); -// boolean flag = f4 > 0.0F; -// boolean flag1 = f2 < 0.0F; -// boolean flag2 = f5 > 0.0F; -// float f6 = flag ? f4 : -f4; -// float f7 = flag1 ? -f2 : f2; -// float f8 = flag2 ? f5 : -f5; -// float f9 = f6 * f3; -// float f10 = f8 * f3; -// LodDirection lodDirection = flag ? EAST : WEST; -// LodDirection direction1 = flag1 ? UP : DOWN; -// LodDirection direction2 = flag2 ? SOUTH : NORTH; -// if (f6 > f8) -// { -// if (f7 > f9) -// { -// return makeDirectionArray(direction1, lodDirection, direction2); -// } -// else -// { -// return f10 > f7 ? makeDirectionArray(lodDirection, direction2, direction1) : makeDirectionArray(lodDirection, direction1, direction2); -// } -// } -// else if (f7 > f10) -// { -// return makeDirectionArray(direction1, direction2, lodDirection); -// } -// else -// { -// return f9 > f7 ? makeDirectionArray(direction2, lodDirection, direction1) : makeDirectionArray(direction2, direction1, lodDirection); -// } -// } - -// private static LodDirection[] makeDirectionArray(LodDirection p_196053_0_, LodDirection p_196053_1_, LodDirection p_196053_2_) -// { -// return new LodDirection[] { p_196053_0_, p_196053_1_, p_196053_2_, p_196053_2_.getOpposite(), p_196053_1_.getOpposite(), p_196053_0_.getOpposite() }; -// } - -// public static LodDirection rotate(Mat4f p_229385_0_, LodDirection p_229385_1_) -// { -// Vec3i Vec3i = p_229385_1_.getNormal(); -// Vector4f vector4f = new Vector4f(Vec3i.getX(), Vec3i.getY(), Vec3i.getZ(), 0.0F); -// vector4f.transform(p_229385_0_); -// return getNearest(vector4f.x(), vector4f.y(), vector4f.z()); -// } - -// public Quaternion getRotation() -// { -// Quaternion quaternion = Vector3f.XP.rotationDegrees(90.0F); -// switch (this) -// { -// case DOWN: -// return Vector3f.XP.rotationDegrees(180.0F); -// case UP: -// return Quaternion.ONE.copy(); -// case NORTH: -// quaternion.mul(Vector3f.ZP.rotationDegrees(180.0F)); -// return quaternion; -// case SOUTH: -// return quaternion; -// case WEST: -// quaternion.mul(Vector3f.ZP.rotationDegrees(90.0F)); -// return quaternion; -// case EAST: -// default: -// quaternion.mul(Vector3f.ZP.rotationDegrees(-90.0F)); -// return quaternion; -// } -// } - -// public int get3DDataValue() -// { -// return this.data3d; -// } -// -// public int get2DDataValue() -// { -// return this.data2d; -// } - - public LodDirection.AxisDirection getAxisDirection() - { - return this.axisDirection; - } - -// public LodDirection getOpposite() -// { -// return from3DDataValue(this.oppositeIndex); -// } - - public LodDirection getClockWise() - { - switch (this) - { - case NORTH: - return EAST; - case SOUTH: - return WEST; - case WEST: - return NORTH; - case EAST: - return SOUTH; - default: - throw new IllegalStateException("Unable to get Y-rotated facing of " + this); - } - } - - public LodDirection getCounterClockWise() - { - switch (this) - { - case NORTH: - return WEST; - case SOUTH: - return EAST; - case WEST: - return SOUTH; - case EAST: - return NORTH; - default: - throw new IllegalStateException("Unable to get CCW facing of " + this); - } - } - - public String getName() - { - return this.name; - } - - public LodDirection.Axis getAxis() - { - return this.axis; - } - - public static LodDirection byName(String name) - { - return name == null ? null : BY_NAME.get(name.toLowerCase(Locale.ROOT)); - } - -// public static LodDirection from3DDataValue(int p_82600_0_) -// { -// return BY_3D_DATA[MathHelper.abs(p_82600_0_ % BY_3D_DATA.length)]; -// } -// -// public static LodDirection from2DDataValue(int p_176731_0_) -// { -// return BY_2D_DATA[MathHelper.abs(p_176731_0_ % BY_2D_DATA.length)]; -// } - -// @Nullable -// public static LodDirection fromNormal(int p_218383_0_, int p_218383_1_, int p_218383_2_) -// { -// return BY_NORMAL.get(BlockPos.asLong(p_218383_0_, p_218383_1_, p_218383_2_)); -// } - -// public static LodDirection fromYRot(double p_176733_0_) -// { -// return from2DDataValue(MathHelper.floor(p_176733_0_ / 90.0D + 0.5D) & 3); -// } - - public static LodDirection fromAxisAndDirection(LodDirection.Axis p_211699_0_, LodDirection.AxisDirection p_211699_1_) - { - switch (p_211699_0_) - { - case X: - return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? EAST : WEST; - case Y: - return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? UP : DOWN; - case Z: - default: - return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? SOUTH : NORTH; - } - } - -// public float toYRot() -// { -// return (this.data2d & 3) * 90; -// } - -// public static LodDirection getRandom(Random p_239631_0_) -// { -// return Util.getRandom(VALUES, p_239631_0_); -// } - -// public static LodDirection getNearest(double p_210769_0_, double p_210769_2_, double p_210769_4_) -// { -// return getNearest((float) p_210769_0_, (float) p_210769_2_, (float) p_210769_4_); -// } - -// public static LodDirection getNearest(float p_176737_0_, float p_176737_1_, float p_176737_2_) -// { -// LodDirection lodDirection = NORTH; -// float f = Float.MIN_VALUE; -// -// for (LodDirection direction1 : VALUES) -// { -// float f1 = p_176737_0_ * direction1.normal.x + p_176737_1_ * direction1.normal.y + p_176737_2_ * direction1.normal.z; -// if (f1 > f) -// { -// f = f1; -// lodDirection = direction1; -// } -// } -// -// return lodDirection; -// } - - public static LodDirection get(LodDirection.AxisDirection p_181076_0_, LodDirection.Axis p_181076_1_) - { - for (LodDirection lodDirection : VALUES) - { - if (lodDirection.getAxisDirection() == p_181076_0_ && lodDirection.getAxis() == p_181076_1_) - { - return lodDirection; - } - } - - throw new IllegalArgumentException("No such direction: " + p_181076_0_ + " " + p_181076_1_); - } - - public Vec3i getNormal() - { - return this.normal; - } - -// public boolean isFacingAngle(float p_243532_1_) -// { -// float f = p_243532_1_ * ((float) Math.PI / 180F); -// float f1 = -MathHelper.sin(f); -// float f2 = MathHelper.cos(f); -// return this.normal.getX() * f1 + this.normal.getZ() * f2 > 0.0F; -// } - - public enum Axis implements Predicate - { - X("x") - { - @Override - public int choose(int x, int y, int z) - { - return x; - } - - @Override - public double choose(double x, double y, double z) - { - return x; - } - }, - Y("y") - { - @Override - public int choose(int x, int y, int z) - { - return y; - } - - @Override - public double choose(double x, double y, double z) - { - return y; - } - }, - Z("z") - { - @Override - public int choose(int x, int y, int z) - { - return z; - } - - @Override - public double choose(double x, double y, double z) - { - return z; - } - }; - - private static final LodDirection.Axis[] VALUES = values(); - - private static final Map BY_NAME = Arrays.stream(VALUES).collect(Collectors.toMap(LodDirection.Axis::getName, (p_199785_0_) -> - { - return p_199785_0_; - })); - private final String name; - - Axis(String name) - { - this.name = name; - } - - public static LodDirection.Axis byName(String name) - { - return BY_NAME.get(name.toLowerCase(Locale.ROOT)); - } - - public String getName() - { - return this.name; - } - - public boolean isVertical() - { - return this == Y; - } - - public boolean isHorizontal() - { - return this == X || this == Z; - } - - @Override - public String toString() - { - return this.name; - } - -// public static LodDirection.Axis getRandom(Random p_239634_0_) -// { -// return Util.getRandom(VALUES, p_239634_0_); -// } - - @Override - public boolean test(LodDirection p_test_1_) - { - return p_test_1_ != null && p_test_1_.getAxis() == this; - } - -// public LodDirection.Plane getPlane() -// { -// switch (this) -// { -// case X: -// case Z: -// return LodDirection.Plane.HORIZONTAL; -// case Y: -// return LodDirection.Plane.VERTICAL; -// default: -// throw new Error("Someone's been tampering with the universe!"); -// } -// } - - public abstract int choose(int p_196052_1_, int p_196052_2_, int p_196052_3_); - - public abstract double choose(double p_196051_1_, double p_196051_3_, double p_196051_5_); - } - - public enum AxisDirection - { - POSITIVE(1, "Towards positive"), - NEGATIVE(-1, "Towards negative"); - - private final int step; - private final String name; - - AxisDirection(int newStep, String newName) - { - this.step = newStep; - this.name = newName; - } - - public int getStep() - { - return this.step; - } - - @Override - public String toString() - { - return this.name; - } - - public LodDirection.AxisDirection opposite() - { - return this == POSITIVE ? NEGATIVE : POSITIVE; - } - } - -// public static enum Plane implements Iterable, Predicate -// { -// HORIZONTAL(new LodDirection[] { LodDirection.NORTH, LodDirection.EAST, LodDirection.SOUTH, LodDirection.WEST }, new LodDirection.Axis[] { LodDirection.Axis.X, LodDirection.Axis.Z }), -// VERTICAL(new LodDirection[] { LodDirection.UP, LodDirection.DOWN }, new LodDirection.Axis[] { LodDirection.Axis.Y }); -// -// private final LodDirection[] faces; -// private final LodDirection.Axis[] axis; -// -// private Plane(LodDirection[] p_i49393_3_, LodDirection.Axis[] p_i49393_4_) -// { -// this.faces = p_i49393_3_; -// this.axis = p_i49393_4_; -// } -// -// public LodDirection getRandomDirection(Random p_179518_1_) -// { -// return Util.getRandom(this.faces, p_179518_1_); -// } -// -// public LodDirection.Axis getRandomAxis(Random p_244803_1_) -// { -// return Util.getRandom(this.axis, p_244803_1_); -// } -// -// @Override -// public boolean test(@Nullable LodDirection p_test_1_) -// { -// return p_test_1_ != null && p_test_1_.getAxis().getPlane() == this; -// } -// -// @Override -// public Iterator iterator() -// { -// return Iterators.forArray(this.faces); -// } -// -// public Stream stream() -// { -// return Arrays.stream(this.faces); -// } -// } - - - - - public String getSerializedName() - { - return this.name; - } - - @Override - public String toString() - { - return this.name; - } - -} diff --git a/src/main/java/com/seibel/lod/core/enums/WorldType.java b/src/main/java/com/seibel/lod/core/enums/WorldType.java deleted file mode 100644 index a36b06194..000000000 --- a/src/main/java/com/seibel/lod/core/enums/WorldType.java +++ /dev/null @@ -1,33 +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.core.enums; - -/** - * ServerWorld, ClientWorld, Unknown - * - * @author James Seibel - * @version 11-12-2021 - */ -public enum WorldType -{ - ServerWorld, - ClientWorld, - Unknown -} diff --git a/src/main/java/com/seibel/lod/core/enums/config/BlocksToAvoid.java b/src/main/java/com/seibel/lod/core/enums/config/BlocksToAvoid.java deleted file mode 100644 index bdfcaf363..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/BlocksToAvoid.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.core.enums.config; - -/** - * heightmap
- * multi_lod
- * - * @author Leonardo Amato - * @version 11-16-2021 - */ -public enum BlocksToAvoid -{ - NONE(false, false), - - NON_FULL(true, false), - - NO_COLLISION(false, true), - - BOTH(true, true); - - public final boolean nonFull; - public final boolean noCollision; - - BlocksToAvoid(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/core/enums/config/BufferRebuildTimes.java b/src/main/java/com/seibel/lod/core/enums/config/BufferRebuildTimes.java deleted file mode 100644 index 3c5db0390..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/BufferRebuildTimes.java +++ /dev/null @@ -1,52 +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.core.enums.config; - -/** - * FREQUENT
- * NORMAL
- * RARE
- *
- * Determines how fast the buffers need to be regenerated - * - * @author Leonardo Amato - * @version 9-25-2021 - */ -public enum BufferRebuildTimes -{ - FREQUENT(1000, 500, 2500, 1), - - NORMAL(2000, 1000, 5000, 4), - - RARE(5000, 2000, 10000, 16); - - public final int playerMoveTimeout; - public final int renderedChunkTimeout; - public final int chunkChangeTimeout; - public final int playerMoveDistance; - - BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout, int playerMoveDistance) - { - this.playerMoveTimeout = playerMoveTimeout; - this.renderedChunkTimeout = renderedChunkTimeout; - this.chunkChangeTimeout = chunkChangeTimeout; - this.playerMoveDistance = playerMoveDistance; - } -} diff --git a/src/main/java/com/seibel/lod/core/enums/config/DistanceGenerationMode.java b/src/main/java/com/seibel/lod/core/enums/config/DistanceGenerationMode.java deleted file mode 100644 index 712de3cc3..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/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.core.enums.config; - -/** - * 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 on a pre-existing world. - * Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) - */ - FULL((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/core/enums/config/GenerationPriority.java b/src/main/java/com/seibel/lod/core/enums/config/GenerationPriority.java deleted file mode 100644 index af2089170..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/GenerationPriority.java +++ /dev/null @@ -1,41 +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.core.enums.config; - -/** - * AUTO
- * Near_First
- * Far_First
- *
- * Determines which LODs should have priority when generating - * outside the normal view distance. - * - * @author Leonardo Amato - * @version 12-1-2021 - */ -public enum GenerationPriority -{ - /** NEAR_FIRST when connected to servers and FAR_FIRST when on single player */ - AUTO, - - NEAR_FIRST, - - FAR_FIRST -} diff --git a/src/main/java/com/seibel/lod/core/enums/config/GpuUploadMethod.java b/src/main/java/com/seibel/lod/core/enums/config/GpuUploadMethod.java deleted file mode 100644 index 4d36d676a..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/GpuUploadMethod.java +++ /dev/null @@ -1,59 +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.core.enums.config; - -/** - * Auto, Buffer_Storage, Sub_Data, Buffer_Mapping, Data - * - * @author James Seibel - * @version 12-1-2021 - */ -public enum GpuUploadMethod -{ - /** Picks the best option based on the GPU the user has. */ - AUTO, - - /** - * Default for NVIDIA if OpenGL 4.5 is supported.
- * Fast rendering, no stuttering. - */ - BUFFER_STORAGE, - - /** - * Backup option for NVIDIA.
- * Fast rendering but may stutter when uploading. - */ - SUB_DATA, - - /** - * Default option for AMD/Intel.
- * May end up storing buffers in System memory.
- * Fast rending if in GPU memory, slow if in system memory,
- * but won't stutter when uploading. - */ - BUFFER_MAPPING, - - /** - * Backup option for AMD/Intel.
- * Fast rendering but may stutter when uploading. - */ - DATA, - -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/enums/config/HorizontalQuality.java b/src/main/java/com/seibel/lod/core/enums/config/HorizontalQuality.java deleted file mode 100644 index e91ed0f68..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/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.core.enums.config; - -/** - * 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/core/enums/config/HorizontalResolution.java b/src/main/java/com/seibel/lod/core/enums/config/HorizontalResolution.java deleted file mode 100644 index 794b67f71..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/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.core.enums.config; - -import java.util.ArrayList; -import java.util.Collections; - -import com.seibel.lod.core.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/core/enums/config/HorizontalScale.java b/src/main/java/com/seibel/lod/core/enums/config/HorizontalScale.java deleted file mode 100644 index e710de006..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/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.core.enums.config; - -/** - * 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/core/enums/config/LodTemplate.java b/src/main/java/com/seibel/lod/core/enums/config/LodTemplate.java deleted file mode 100644 index 397699c7d..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/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.core.enums.config; - -import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.AbstractLodTemplate; -import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.CubicLodTemplate; -import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.DynamicLodTemplate; -import com.seibel.lod.core.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/core/enums/config/ShadingMode.java b/src/main/java/com/seibel/lod/core/enums/config/ShadingMode.java deleted file mode 100644 index 1971d51bb..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/ShadingMode.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.seibel.lod.core.enums.config; - -/** - * 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/core/enums/config/VanillaOverdraw.java b/src/main/java/com/seibel/lod/core/enums/config/VanillaOverdraw.java deleted file mode 100644 index e28552893..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/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.core.enums.config; - -/** - * 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/core/enums/config/VerticalQuality.java b/src/main/java/com/seibel/lod/core/enums/config/VerticalQuality.java deleted file mode 100644 index 9d82f9486..000000000 --- a/src/main/java/com/seibel/lod/core/enums/config/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.core.enums.config; - -/** - * 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/core/enums/rendering/DebugMode.java b/src/main/java/com/seibel/lod/core/enums/rendering/DebugMode.java deleted file mode 100644 index cdc02fa1f..000000000 --- a/src/main/java/com/seibel/lod/core/enums/rendering/DebugMode.java +++ /dev/null @@ -1,54 +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.core.enums.rendering; - -/** - * off, detail, detail wireframe - * - * @author James Seibel - * @version 8-28-2021 - */ -public enum DebugMode -{ - /** 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 - { - OFF.next = SHOW_DETAIL; - SHOW_DETAIL.next = SHOW_DETAIL_WIREFRAME; - SHOW_DETAIL_WIREFRAME.next = OFF; - } - - /** returns the next debug mode */ - public DebugMode getNext() - { - return this.next; - } -} diff --git a/src/main/java/com/seibel/lod/core/enums/rendering/FogColorMode.java b/src/main/java/com/seibel/lod/core/enums/rendering/FogColorMode.java deleted file mode 100644 index 14b68004c..000000000 --- a/src/main/java/com/seibel/lod/core/enums/rendering/FogColorMode.java +++ /dev/null @@ -1,42 +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.core.enums.rendering; - -/** - * USE_DEFAULT_FOG_COLOR,
- * USE_SKY_COLOR,
- * - * @author James Seibel - * @version 11-27-2021 - */ -public enum FogColorMode -{ - /** Fog uses Minecraft's fog color. */ - USE_WORLD_FOG_COLOR, - - /** - * Replicates the effect of the clear sky mod. - * Making the fog blend in with the sky better - * https://www.curseforge.com/minecraft/mc-mods/clear-skies - * https://www.curseforge.com/minecraft/mc-mods/clear-skies-forge-port - * For it to look good you need one of those mods - */ - USE_SKY_COLOR, -} diff --git a/src/main/java/com/seibel/lod/core/enums/rendering/FogDistance.java b/src/main/java/com/seibel/lod/core/enums/rendering/FogDistance.java deleted file mode 100644 index a73443ca4..000000000 --- a/src/main/java/com/seibel/lod/core/enums/rendering/FogDistance.java +++ /dev/null @@ -1,33 +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.core.enums.rendering; - -/** - * NEAR, FAR, or NEAR_AND_FAR. - * - * @author James Seibel - * @version 11-26-2021 - */ -public enum FogDistance -{ - NEAR, - FAR, - NEAR_AND_FAR -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/enums/rendering/FogDrawMode.java b/src/main/java/com/seibel/lod/core/enums/rendering/FogDrawMode.java deleted file mode 100644 index bf05d7dbe..000000000 --- a/src/main/java/com/seibel/lod/core/enums/rendering/FogDrawMode.java +++ /dev/null @@ -1,40 +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.core.enums.rendering; - -/** - * USE_OPTIFINE_FOG_SETTING,
- * FOG_ENABLED,
- * FOG_DISABLED
- * - * @author James Seibel - * @version 11-27-2021 - */ -public enum FogDrawMode -{ - /** - * Use whatever Fog setting optifine is using. - * If optifine isn't installed this defaults to ALWAYS_DRAW_FOG. - */ - USE_OPTIFINE_SETTING, - - FOG_ENABLED, - FOG_DISABLED -} diff --git a/src/main/java/com/seibel/lod/core/enums/rendering/GLProxyContext.java b/src/main/java/com/seibel/lod/core/enums/rendering/GLProxyContext.java deleted file mode 100644 index b83bcc6c3..000000000 --- a/src/main/java/com/seibel/lod/core/enums/rendering/GLProxyContext.java +++ /dev/null @@ -1,41 +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.core.enums.rendering; - -/** - * 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, - - /** A context that can be used for miscellaneous tasks, owned by the GLProxy */ - PROXY_WORKER, - - /** used to un-bind threads */ - NONE, -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/handlers/ChunkFileLoader.java b/src/main/java/com/seibel/lod/core/handlers/ChunkFileLoader.java deleted file mode 100644 index 81cb0068e..000000000 --- a/src/main/java/com/seibel/lod/core/handlers/ChunkFileLoader.java +++ /dev/null @@ -1,65 +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.core.handlers; - -/** - * - * @author Cola - * @author Leonardo Amato - * @version 11-12-2021 - */ -public class ChunkFileLoader -{ - // TODO -// public static IChunk getChunkFromFile(ChunkPos pos) -// { -// LevelWrapper clientLevel = MinecraftWrapper.INSTANCE.getWrappedClientLevel(); -// if (clientLevel == null) -// return null; -// WorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(clientLevel.getDimensionType()); -// try -// { -// File file = new File(serverWorld.getSaveFolder().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/core/handlers/IReflectionHandler.java b/src/main/java/com/seibel/lod/core/handlers/IReflectionHandler.java deleted file mode 100644 index 6e7aa1c2c..000000000 --- a/src/main/java/com/seibel/lod/core/handlers/IReflectionHandler.java +++ /dev/null @@ -1,58 +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.core.handlers; - -import com.seibel.lod.core.enums.rendering.FogDrawMode; -import com.seibel.lod.core.objects.math.Mat4f; - -/** - * A singleton used to get variables from methods - * where they are private or potentially absent. - * Specifically the fog setting used by Optifine or the - * presence/absence of other mods. - *

- * This interface doesn't necessarily have to exist, but - * it makes using the singleton handler more uniform (always - * passing in interfaces), and it may be needed in the future if - * we find that reflection handlers need to be different for - * different MC versions. - * - * @author James Seibel - * @version 11-26-2021 - */ -public interface IReflectionHandler -{ - /** @returns Whether Optifine is set to render fog or not. */ - FogDrawMode getFogDrawMode(); - - /** @returns if Vivecraft is present. Attempts to find the "VRRenderer" class. */ - boolean vivecraftPresent(); - - /** - * Modifies the projection matrix's clip planes. - * The projection matrix must be in column-major format. - * - * @param projectionMatrix The projection matrix to be modified. - * @param newNearClipPlane the new near clip plane value. - * @param newFarClipPlane the new far clip plane value. - * @return The modified matrix. - */ - Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane); -} diff --git a/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHandler.java deleted file mode 100644 index f8e5e1f1e..000000000 --- a/src/main/java/com/seibel/lod/core/handlers/LodDimensionFileHandler.java +++ /dev/null @@ -1,485 +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.core.handlers; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.Arrays; -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.core.api.ClientApi; -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.enums.config.VerticalQuality; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.objects.lod.LodRegion; -import com.seibel.lod.core.objects.lod.RegionPos; -import com.seibel.lod.core.objects.lod.VerticalLevelContainer; -import com.seibel.lod.core.util.LodThreadFactory; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.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 = 7; - - /** - * 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 - VerticalQuality tempVerticalQuality = VerticalQuality.HIGH; - do { - DistanceGenerationMode tempGenMode = DistanceGenerationMode.FULL; - do { - fileName = getFileNameAndPathForRegion(regionX, regionZ, tempGenMode, tempDetailLevel, verticalQuality); - if (fileName != null) - { - file = new File(fileName); - if (file.exists()) - break; - } - //decrease gen mode - if (tempGenMode == DistanceGenerationMode.FULL) - 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; - } while (tempGenMode != generationMode); - if (fileName != null) - { - file = new File(fileName); - if (file.exists()) - break; - } - if (tempVerticalQuality == VerticalQuality.HIGH) - tempVerticalQuality = VerticalQuality.MEDIUM; - else if (tempVerticalQuality == VerticalQuality.MEDIUM) - tempVerticalQuality = VerticalQuality.LOW; - } while (tempVerticalQuality != verticalQuality); - 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 < 6) - { - // the file we are reading is an older version, - // close the reader and delete the file. - inputStream.close(); - file.delete(); - ClientApi.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(); - ClientApi.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; - } - else if (fileVersion == 6) - { - //this is old, but readable version - byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel); - inputStream.read(data); - inputStream.close(); - // add the data to our region - region.addLevelContainer(new VerticalLevelContainer(data, 6)); - } else - { - // 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, LOD_SAVE_FILE_VERSION)); - } - } - catch (IOException ioEx) - { - ClientApi.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 - ClientApi.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) - { - ClientApi.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 - ClientApi.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) - { - ClientApi.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: "); - e.printStackTrace(); - } - } - } - - - public void saveRegionFile (byte[] regionFile, RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality) - { - int regionX = regionPos.x; - int regionZ = regionPos.z; - String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality); - - if (fileName != null) - { - File oldFile = new File(fileName); - File newFile = new File(fileName + TMP_FILE_EXTENSION); - try (OutputStream os = new FileOutputStream(newFile)) - { - os.write(regionFile); - Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); - os.close(); - } - catch (IOException ioEx) - { - ClientApi.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + ioEx.getMessage() + "]: "); - ioEx.printStackTrace(); - } - } - } - - public byte[] getRegionFile (RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality) - { - int regionX = regionPos.x; - int regionZ = regionPos.z; - String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality); - if (fileName != null) - { - File file = new File(fileName); - try (InputStream is = new FileInputStream(file)) - { - byte[] data = ThreadMapUtil.getSaveContainer(detailLevel); - is.read(data); - is.close(); - return Arrays.copyOf(data, (int) file.length()); - } - catch (IOException ioEx) - { - ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: "); - ioEx.printStackTrace(); - } - } - return new byte[0]; - } - - - //================// - // helper methods // - //================// - - public int getHashFromFile(RegionPos regionPos, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality) - { - int regionX = regionPos.x; - int regionZ = regionPos.z; - String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality); - if (fileName == null) - return 0; - - File file = new File(fileName); - return file.hashCode(); - } - - - /** - * 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) - { - ClientApi.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/core/handlers/ReflectionHandler.java b/src/main/java/com/seibel/lod/core/handlers/ReflectionHandler.java deleted file mode 100644 index a096bb71e..000000000 --- a/src/main/java/com/seibel/lod/core/handlers/ReflectionHandler.java +++ /dev/null @@ -1,195 +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.core.handlers; - -import java.lang.reflect.Field; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.seibel.lod.core.ModInfo; -import com.seibel.lod.core.enums.rendering.FogDrawMode; -import com.seibel.lod.core.objects.math.Mat4f; - -/** - * A singleton used to get variables from methods - * where they are private or potentially absent. - * Specifically the fog setting in Optifine or the - * presence/absence of other mods. - * - * @author James Seibel - * @version 11-26-2021 - */ -public class ReflectionHandler implements IReflectionHandler -{ - private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME + "-" + ReflectionHandler.class.getSimpleName()); - - private static ReflectionHandler instance; - - private Field ofFogField = null; - private final Object mcOptionsObject; - - - - private ReflectionHandler(Field[] optionFields, Object newMcOptionsObject) - { - mcOptionsObject = newMcOptionsObject; - - setupFogField(optionFields); - } - - /** - * @param optionFields the fields that should contain "ofFogType" - * @param newMcOptionsObject the object instance that contains "ofFogType" - * @return the ReflectionHandler just created - * @throws IllegalStateException if a ReflectionHandler already exists - */ - public static ReflectionHandler createSingleton(Field[] optionFields, Object newMcOptionsObject) throws IllegalStateException - { - if (instance != null) - { - throw new IllegalStateException(); - } - - instance = new ReflectionHandler(optionFields, newMcOptionsObject); - return instance; - } - - - - - /** finds the Optifine fog type field */ - private void setupFogField(Field[] optionFields) - { - // 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 - 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 - */ - @Override - public FogDrawMode getFogDrawMode() - { - if (ofFogField == null) - { - // either optifine isn't installed, - // the variable name was changed, or - // the setup method wasn't called yet. - return FogDrawMode.FOG_ENABLED; - } - - int returnNum = 0; - - try - { - returnNum = (int) ofFogField.get(mcOptionsObject); - } - 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: // fast - case 2: // fancy - return FogDrawMode.FOG_ENABLED; - case 3: // off - return FogDrawMode.FOG_DISABLED; - } - } - - - - /** Detect if Vivecraft is present. Attempts to find the "VRRenderer" class. */ - @Override - public boolean vivecraftPresent() - { - try - { - Class.forName("org.vivecraft.provider.VRRenderer"); - return true; - } - catch (ClassNotFoundException ignored) - { - LOGGER.info(ReflectionHandler.class.getSimpleName() + ": Vivecraft not detected."); - } - return false; - } - - /** - * Modifies the projection matrix's clip planes. - * The projection matrix must be in column-major format. - * - * @param projectionMatrix The projection matrix to be modified. - * @param newNearClipPlane the new near clip plane value. - * @param newFarClipPlane the new far clip plane value. - * @return The modified matrix. - */ - @Override - public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane) - { - // find the matrix values. - float nearMatrixValue = -((newFarClipPlane + newNearClipPlane) / (newFarClipPlane - newNearClipPlane)); - float farMatrixValue = -((2 * newFarClipPlane * newNearClipPlane) / (newFarClipPlane - newNearClipPlane)); - - try - { - // TODO this was originally created before we had the Mat4f object, - // so this doesn't need to be done with reflection anymore. - // And should be moved to RenderUtil - - // 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, nearMatrixValue); - fields[11].set(projectionMatrix, farMatrixValue); - } - catch (Exception e) - { - e.printStackTrace(); - } - return projectionMatrix; - } -} diff --git a/src/main/java/com/seibel/lod/core/objects/MinDefaultMax.java b/src/main/java/com/seibel/lod/core/objects/MinDefaultMax.java deleted file mode 100644 index 977c3533d..000000000 --- a/src/main/java/com/seibel/lod/core/objects/MinDefaultMax.java +++ /dev/null @@ -1,41 +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.core.objects; - -/** - * Used when setting up configuration fields. - * - * @author James Seibel - * @version 11-14-2021 - * @param The data type this object is storing - */ -public class MinDefaultMax -{ - public final T minValue; - public final T defaultValue; - public final T maxValue; - - public MinDefaultMax(T newMinValue, T newDefaultValue, T newMaxValue) - { - minValue = newMinValue; - defaultValue = newDefaultValue; - maxValue = newMaxValue; - } -} diff --git a/src/main/java/com/seibel/lod/core/objects/PosToGenerateContainer.java b/src/main/java/com/seibel/lod/core/objects/PosToGenerateContainer.java deleted file mode 100644 index 730c9d6cb..000000000 --- a/src/main/java/com/seibel/lod/core/objects/PosToGenerateContainer.java +++ /dev/null @@ -1,209 +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.core.objects; - -import com.seibel.lod.core.util.LevelPosUtil; - -/** - * Holds the levelPos that need to be generated. - * - * @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/core/objects/PosToRenderContainer.java b/src/main/java/com/seibel/lod/core/objects/PosToRenderContainer.java deleted file mode 100644 index 5a42e7212..000000000 --- a/src/main/java/com/seibel/lod/core/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.core.objects; - -import java.util.Arrays; - -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodUtil; - -/** - * Holds a levelPos that needs to be rendered. - * - * @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; - 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 - ClientApi.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/core/objects/VertexOptimizer.java b/src/main/java/com/seibel/lod/core/objects/VertexOptimizer.java deleted file mode 100644 index 4c13f247b..000000000 --- a/src/main/java/com/seibel/lod/core/objects/VertexOptimizer.java +++ /dev/null @@ -1,626 +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.core.objects; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.enums.rendering.DebugMode; -import com.seibel.lod.core.objects.math.Vec3i; -import com.seibel.lod.core.util.ColorUtil; -import com.seibel.lod.core.util.DataPointUtil; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; - -/** - * This class handles all the vertex optimization that's needed for a column of lods. W - * @author Leonardo Amato - * @version 10-2-2021 - */ -public class VertexOptimizer -{ - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - - 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 LodDirection[] DIRECTIONS = new LodDirection[] { - LodDirection.UP, - LodDirection.DOWN, - LodDirection.WEST, - LodDirection.EAST, - LodDirection.NORTH, - LodDirection.SOUTH }; - - /** North, South, East, West */ - public static final LodDirection[] ADJ_DIRECTIONS = new LodDirection[] { - LodDirection.EAST, - LodDirection.WEST, - LodDirection.SOUTH, - LodDirection.NORTH }; - - /** All the faces and vertices of a cube. This is used to extract the vertex from the column */ - @SuppressWarnings("serial") - public static final Map DIRECTION_VERTEX_MAP = new HashMap() - {{ - put(LodDirection.UP, new int[][] { - { 0, 1, 0 }, // 0 - { 0, 1, 1 }, // 1 - { 1, 1, 1 }, // 2 - - { 0, 1, 0 }, // 0 - { 1, 1, 1 }, // 2 - { 1, 1, 0 } // 3 - }); - put(LodDirection.DOWN, new int[][] { - { 1, 0, 0 }, // 0 - { 1, 0, 1 }, // 1 - { 0, 0, 1 }, // 2 - - { 1, 0, 0 }, // 0 - { 0, 0, 1 }, // 2 - { 0, 0, 0 } // 3 - }); - put(LodDirection.EAST, new int[][] { - { 1, 1, 0 }, // 0 - { 1, 1, 1 }, // 1 - { 1, 0, 1 }, // 2 - - { 1, 1, 0 }, // 0 - { 1, 0, 1 }, // 2 - { 1, 0, 0 } }); // 3 - put(LodDirection.WEST, new int[][] { - { 0, 0, 0 }, // 0 - { 0, 0, 1 }, // 1 - { 0, 1, 1 }, // 2 - - { 0, 0, 0 }, // 0 - { 0, 1, 1 }, // 2 - { 0, 1, 0 } // 3 - }); - put(LodDirection.SOUTH, new int[][] { - { 1, 0, 1 }, // 0 - { 1, 1, 1 }, // 1 - { 0, 1, 1 }, // 2 - - { 1, 0, 1 }, // 0 - { 0, 1, 1 }, // 2 - { 0, 0, 1 } // 3 - }); - put(LodDirection.NORTH, new int[][] { - { 0, 0, 0 }, // 0 - { 0, 1, 0 }, // 1 - { 1, 1, 0 }, // 2 - - { 0, 0, 0 }, // 0 - { 1, 1, 0 }, // 2 - { 1, 0, 0 } // 3 - }); - }}; - - - /** - * This indicates which position is invariable in the DIRECTION_VERTEX_MAP. - * Is used to extract the vertex - */ - @SuppressWarnings("serial") - public static final Map FACE_DIRECTION = new HashMap() - {{ - put(LodDirection.UP, new int[] { Y, MAX }); - put(LodDirection.DOWN, new int[] { Y, MIN }); - put(LodDirection.EAST, new int[] { X, MAX }); - put(LodDirection.WEST, new int[] { X, MIN }); - put(LodDirection.SOUTH, new int[] { Z, MAX }); - put(LodDirection.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 - */ - @SuppressWarnings("serial") - public static final Map DIRECTION_NORMAL_MAP = new HashMap() - {{ - put(LodDirection.UP, LodDirection.UP.getNormal()); - put(LodDirection.DOWN, LodDirection.DOWN.getNormal()); - put(LodDirection.EAST, LodDirection.EAST.getNormal()); - put(LodDirection.WEST, LodDirection.WEST.getNormal()); - put(LodDirection.SOUTH, LodDirection.SOUTH.getNormal()); - put(LodDirection.NORTH, LodDirection.NORTH.getNormal()); - }}; - - /** We use this index for all array that are going to */ - @SuppressWarnings("serial") - public static final Map DIRECTION_INDEX = new HashMap() - {{ - put(LodDirection.UP, 0); - put(LodDirection.DOWN, 1); - put(LodDirection.EAST, 2); - put(LodDirection.WEST, 3); - put(LodDirection.SOUTH, 4); - put(LodDirection.NORTH, 5); - }}; - - @SuppressWarnings("serial") - public static final Map ADJ_DIRECTION_INDEX = new HashMap() - {{ - put(LodDirection.EAST, 0); - put(LodDirection.WEST, 1); - put(LodDirection.SOUTH, 2); - put(LodDirection.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 */ - @SuppressWarnings("serial") - public VertexOptimizer() - { - boxOffset = new int[3]; - boxWidth = new int[3]; - - colorMap = new int[6]; - skyLights = new HashMap() - {{ - put(LodDirection.UP, new byte[1]); - put(LodDirection.DOWN, new byte[1]); - put(LodDirection.EAST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.WEST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.SOUTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.NORTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - }}; - adjHeight = new HashMap() - {{ - put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - }}; - adjDepth = new HashMap() - {{ - put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]); - put(LodDirection.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(LodDirection.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 (LodDirection lodDirection : DIRECTIONS) - { - if (!adjShadeDisabled[DIRECTION_INDEX.get(lodDirection)]) - colorMap[DIRECTION_INDEX.get(lodDirection)] = ColorUtil.applyShade(color, MC.getShade(lodDirection)); - else - colorMap[DIRECTION_INDEX.get(lodDirection)] = color; - } - } - - /** - * @param lodDirection of the face of which we want to get the color - * @return color of the face - */ - public int getColor(LodDirection lodDirection) - { - if (CONFIG.client().advanced().debugging().getDebugMode() != DebugMode.SHOW_DETAIL) - return colorMap[DIRECTION_INDEX.get(lodDirection)]; - else - return ColorUtil.applyShade(color, MC.getShade(lodDirection)); - } - - /** - */ - public byte getSkyLight(LodDirection lodDirection, int verticalIndex) - { - if(lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN) - return skyLights.get(lodDirection)[0]; - else - return skyLights.get(lodDirection)[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 (LodDirection lodDirection : ADJ_DIRECTIONS) - { - for (int i = 0; i < adjHeight.get(lodDirection).length; i++) - { - adjHeight.get(lodDirection)[i] = VOID_FACE; - adjDepth.get(lodDirection)[i] = VOID_FACE; - skyLights.get(lodDirection)[i] = 0; - } - } - } - - /** determine which faces should be culled */ - public void setUpCulling(int cullingDistance, AbstractBlockPosWrapper playerPos) - { - for (LodDirection lodDirection : DIRECTIONS) - { - if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.WEST || lodDirection == LodDirection.NORTH) - culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) > getFacePos(lodDirection) + cullingDistance; - - else if (lodDirection == LodDirection.UP || lodDirection == LodDirection.EAST || lodDirection == LodDirection.SOUTH) - culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) < getFacePos(lodDirection) - cullingDistance; - - culling[DIRECTION_INDEX.get(lodDirection)] = false; - } - } - - /** - * @param lodDirection 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(LodDirection lodDirection) - { - return culling[DIRECTION_INDEX.get(lodDirection)]; - } - - - /** - * 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(LodDirection.DOWN)[0]; - if(DataPointUtil.doesItExist(singleAdjDataPoint)) - skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - else - skyLights.get(LodDirection.DOWN)[0] = skyLights.get(LodDirection.UP)[0]; - //other sided - //TODO clean some similar cases - for (LodDirection lodDirection : ADJ_DIRECTIONS) - { - if (isCulled(lodDirection)) - continue; - - long[] dataPoint = adjData.get(lodDirection); - if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0])) - { - adjHeight.get(lodDirection)[0] = maxY; - adjDepth.get(lodDirection)[0] = minY; - adjHeight.get(lodDirection)[1] = VOID_FACE; - adjDepth.get(lodDirection)[1] = VOID_FACE; - skyLights.get(lodDirection)[0] = 15; //in void set full skylight - 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(lodDirection)[0] = getMaxY(); - adjDepth.get(lodDirection)[0] = getMinY(); - skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0]; - } - else - { - adjDepth.get(lodDirection)[faceToDraw] = getMinY(); - skyLights.get(lodDirection)[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(lodDirection)[0] = VOID_FACE; - adjDepth.get(lodDirection)[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(lodDirection)[0] = getMaxY(); - adjDepth.get(lodDirection)[0] = height; - skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0]; - } - else - { - adjDepth.get(lodDirection)[faceToDraw] = height; - skyLights.get(lodDirection)[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(lodDirection)[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(lodDirection)[0] = getMaxY(); - } - - adjDepth.get(lodDirection)[faceToDraw] = height; - skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - faceToDraw++; - adjHeight.get(lodDirection)[faceToDraw] = depth; - firstFace = false; - toFinish = true; - toFinishIndex = i + 1; - } - } - } - - if (allAbove) - { - adjHeight.get(lodDirection)[0] = getMaxY(); - adjDepth.get(lodDirection)[0] = getMinY(); - skyLights.get(lodDirection)[0] = skyLights.get(LodDirection.UP)[0]; - faceToDraw++; - } - else if (toFinish) - { - adjDepth.get(lodDirection)[faceToDraw] = minY; - if(toFinishIndex < dataPoint.length) - { - singleAdjDataPoint = dataPoint[toFinishIndex]; - if (DataPointUtil.doesItExist(singleAdjDataPoint)) - skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); - else - skyLights.get(lodDirection)[faceToDraw] = skyLights.get(LodDirection.UP)[0]; - } - faceToDraw++; - } - - adjHeight.get(lodDirection)[faceToDraw] = VOID_FACE; - adjDepth.get(lodDirection)[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 lodDirection that we want to check - * @return position in the axis of the face - */ - public int getFacePos(LodDirection lodDirection) - { - return boxOffset[FACE_DIRECTION.get(lodDirection)[0]] + boxWidth[FACE_DIRECTION.get(lodDirection)[0]] * FACE_DIRECTION.get(lodDirection)[1]; - } - - /** - * returns true if the given direction should be rendered. - */ - public boolean shouldRenderFace(LodDirection lodDirection, int adjIndex) - { - if (lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN) - return adjIndex == 0; - return !(adjHeight.get(lodDirection)[adjIndex] == VOID_FACE && adjDepth.get(lodDirection)[adjIndex] == VOID_FACE); - } - - - /** - * @param lodDirection 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(LodDirection lodDirection, int vertexIndex) - { - return boxOffset[X] + boxWidth[X] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][X]; - } - - /** - * @param lodDirection 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(LodDirection lodDirection, int vertexIndex) - { - return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y]; - } - - /** - * @param lodDirection 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(LodDirection lodDirection, int vertexIndex, int adjIndex) - { - if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.UP) - return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y]; - else - { - // this could probably be cleaned up more, - // but it still works - if (1 - DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y] == ADJACENT_HEIGHT_INDEX) - return adjHeight.get(lodDirection)[adjIndex]; - else - return adjDepth.get(lodDirection)[adjIndex]; - } - } - - /** - * @param lodDirection 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(LodDirection lodDirection, int vertexIndex) - { - return boxOffset[Z] + boxWidth[Z] * DIRECTION_VERTEX_MAP.get(lodDirection)[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/core/objects/lod/LevelContainer.java b/src/main/java/com/seibel/lod/core/objects/lod/LevelContainer.java deleted file mode 100644 index b314fbcf7..000000000 --- a/src/main/java/com/seibel/lod/core/objects/lod/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.core.objects.lod; - -/** - * 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/core/objects/lod/LodDimension.java b/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java deleted file mode 100644 index 06c8fd921..000000000 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodDimension.java +++ /dev/null @@ -1,913 +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.core.objects.lod; - -import java.io.File; -import java.io.IOException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.enums.config.GenerationPriority; -import com.seibel.lod.core.enums.config.VerticalQuality; -import com.seibel.lod.core.handlers.LodDimensionFileHandler; -import com.seibel.lod.core.objects.PosToGenerateContainer; -import com.seibel.lod.core.objects.PosToRenderContainer; -import com.seibel.lod.core.util.DataPointUtil; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodThreadFactory; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - - - -/** - * 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 11-12-2021 - */ -public class LodDimension -{ - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class); - - public final IDimensionTypeWrapper 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 AbstractChunkPosWrapper lastExpandedChunk; - /** prevents the cutAndExpandThread from cutting at the same location multiple times */ - private volatile AbstractChunkPosWrapper 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(IDimensionTypeWrapper newDimension, LodWorld lodWorld, int newWidth) - { - lastCutChunk = null; - lastExpandedChunk = null; - dimension = newDimension; - width = newWidth; - halfWidth = width / 2; - - if (newDimension != null && lodWorld != null) - { - try - { - // determine the save folder - File saveDir; - if (MC.hasSinglePlayerServer()) - { - // local world - - IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(newDimension); - saveDir = new File(serverWorld.getSaveFolder().getCanonicalFile().getPath() + File.separatorChar + "lod"); - } - else - { - // connected to server - - saveDir = new File(MC.getGameDirectory().getCanonicalFile().getPath() + - File.separatorChar + "Distant_Horizons_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) - { - AbstractChunkPosWrapper newPlayerChunk = FACTORY.createChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ)); - - if (lastCutChunk == null) - lastCutChunk = FACTORY.createChunkPos(newPlayerChunk.getX() + 1, newPlayerChunk.getZ() - 1); - - // don't run the tree cutter multiple times - // for the same location - if (newPlayerChunk.getX() != lastCutChunk.getX() || newPlayerChunk.getZ() != lastCutChunk.getZ()) - { - 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 = CONFIG.client().worldGenerator().getDistanceGenerationMode(); - AbstractChunkPosWrapper newPlayerChunk = FACTORY.createChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ)); - VerticalQuality verticalQuality = CONFIG.client().graphics().quality().getVerticalQuality(); - - - if (lastExpandedChunk == null) - lastExpandedChunk = FACTORY.createChunkPos(newPlayerChunk.getX() + 1, newPlayerChunk.getZ() - 1); - - // don't run the expander multiple times - // for the same location - if (newPlayerChunk.getX() != lastExpandedChunk.getX() || newPlayerChunk.getZ() != lastExpandedChunk.getZ()) - { - 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 (CONFIG.client().worldGenerator().getGenerationPriority()) - { - 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 = CONFIG.client().worldGenerator().getDistanceGenerationMode().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); - - // use FAR_FIRST on local worlds and NEAR_FIRST on servers - GenerationPriority generationPriority = CONFIG.client().worldGenerator().getGenerationPriority() == GenerationPriority.AUTO && MC.hasSinglePlayerServer() ? GenerationPriority.FAR_FIRST : GenerationPriority.NEAR_FIRST; - boolean requireCorrectDetailLevel = generationPriority == GenerationPriority.NEAR_FIRST; - - if (region != null) - region.getPosToRender(posToRender, playerPosX, playerPosZ, requireCorrectDetailLevel); - } - - /** - * 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 xChunkPosWrapper, int zChunkPosWrapper) - //{ - // - // LodRegion region = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPosWrapper, zChunkPosWrapper); - // if (region == null) - // return false; - // - // return region.isChunkPreGenerated(xChunkPosWrapper, zChunkPosWrapper); - //} - - /** - * 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/core/objects/lod/LodRegion.java b/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java deleted file mode 100644 index 7acd6b8cf..000000000 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java +++ /dev/null @@ -1,611 +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.core.objects.lod; - -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.enums.config.VerticalQuality; -import com.seibel.lod.core.objects.PosToGenerateContainer; -import com.seibel.lod.core.objects.PosToRenderContainer; -import com.seibel.lod.core.util.DataPointUtil; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.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/core/objects/lod/LodWorld.java b/src/main/java/com/seibel/lod/core/objects/lod/LodWorld.java deleted file mode 100644 index 9897d297d..000000000 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodWorld.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.core.objects.lod; - -import java.util.Hashtable; -import java.util.Map; - -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; - -/** - * 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(newDimension.dimension, newDimension); - } - - /** - * Returns null if no LodDimension exists for the given dimension - */ - public LodDimension getLodDimension(IDimensionTypeWrapper dimType) - { - if (lodDimensions == null) - return null; - - return lodDimensions.get(dimType); - } - - /** - * Resizes the max width in regions that each LodDimension - * should use. - */ - public void resizeDimensionRegionWidth(int newRegionWidth) - { - if (lodDimensions == null) - return; - - saveAllDimensions(); - - for (IDimensionTypeWrapper 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 - ClientApi.LOGGER.info("Saving LODs"); - - for (IDimensionTypeWrapper 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/core/objects/lod/RegionPos.java b/src/main/java/com/seibel/lod/core/objects/lod/RegionPos.java deleted file mode 100644 index f50754d02..000000000 --- a/src/main/java/com/seibel/lod/core/objects/lod/RegionPos.java +++ /dev/null @@ -1,91 +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.core.objects.lod; - -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; - -/** - * This object is similar to ChunkPos or BlockPos. - * - * @author James Seibel - * @version 8-21-2021 - */ -public class RegionPos -{ - private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class); - - - public int x; - public int z; - - - /** 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(AbstractBlockPosWrapper pos) - { - this(WRAPPER_FACTORY.createChunkPos(pos)); - } - - /** Converts from a ChunkPos to a RegionPos */ - public RegionPos(AbstractChunkPosWrapper 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 AbstractChunkPosWrapper chunkPos() - { - return WRAPPER_FACTORY.createChunkPos( - (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 AbstractBlockPosWrapper 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/core/objects/lod/VerticalLevelContainer.java b/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java deleted file mode 100644 index f813a9a86..000000000 --- a/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java +++ /dev/null @@ -1,289 +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.core.objects.lod; - -import com.seibel.lod.core.util.DataPointUtil; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.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 version) - { - int tempMaxVerticalData; - int tempIndex; - int index = 0; - long newData; - detailLevel = inputData[index]; - index++; - tempMaxVerticalData = inputData[index] & 0b01111111; - index++; - size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - int x = size * size * tempMaxVerticalData; - long[] tempDataContainer = new long[x]; - - if (version == 6) - { - 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; - - newData = DataPointUtil.createDataPoint( - DataPointUtil.getAlpha(newData), - DataPointUtil.getRed(newData), - DataPointUtil.getGreen(newData), - DataPointUtil.getBlue(newData), - DataPointUtil.getHeight(newData) - DataPointUtil.VERTICAL_OFFSET, - DataPointUtil.getDepth(newData) - DataPointUtil.VERTICAL_OFFSET, - DataPointUtil.getLightSky(newData), - DataPointUtil.getLightBlock(newData), - DataPointUtil.getGenerationMode(newData), - DataPointUtil.getFlag(newData) - ); - tempDataContainer[i] = newData; - } - } - else //if (version == 7) - { - 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; - tempDataContainer[i] = newData; - } - } - - if (tempMaxVerticalData > DetailDistanceUtil.getMaxVerticalData(detailLevel)) - { - int tempMaxVerticalData2 = DetailDistanceUtil.getMaxVerticalData(detailLevel); - long[] dataToMerge = new long[tempMaxVerticalData]; - long[] tempDataContainer2 = new long[size * size * tempMaxVerticalData2]; - for (int i = 0; i < size * size; i++) - { - System.arraycopy(tempDataContainer, i * tempMaxVerticalData, dataToMerge, 0, tempMaxVerticalData); - dataToMerge = DataPointUtil.mergeMultiData(dataToMerge, tempMaxVerticalData, tempMaxVerticalData2); - System.arraycopy(dataToMerge, 0, tempDataContainer2, i * tempMaxVerticalData2, tempMaxVerticalData2); - } - maxVerticalData = tempMaxVerticalData2; - this.dataContainer = tempDataContainer2; - } - else - { - maxVerticalData = tempMaxVerticalData; - this.dataContainer = tempDataContainer; - } - } - - @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/core/objects/math/Mat4f.java b/src/main/java/com/seibel/lod/core/objects/math/Mat4f.java deleted file mode 100644 index 88588131e..000000000 --- a/src/main/java/com/seibel/lod/core/objects/math/Mat4f.java +++ /dev/null @@ -1,543 +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.core.objects.math; - -import java.nio.FloatBuffer; - -/** - * A (almost) exact copy of Minecraft's 1.16.5 - * implementation of a 4x4 float matrix. - * - * @author James Seibel - * @version 11-11-2021 - */ -public class Mat4f -{ - private float m00; - private float m01; - private float m02; - private float m03; - private float m10; - private float m11; - private float m12; - private float m13; - private float m20; - private float m21; - private float m22; - private float m23; - private float m30; - private float m31; - private float m32; - private float m33; - - - public Mat4f() - { - - } - - public Mat4f(Mat4f sourceMatrix) - { - this.m00 = sourceMatrix.m00; - this.m01 = sourceMatrix.m01; - this.m02 = sourceMatrix.m02; - this.m03 = sourceMatrix.m03; - this.m10 = sourceMatrix.m10; - this.m11 = sourceMatrix.m11; - this.m12 = sourceMatrix.m12; - this.m13 = sourceMatrix.m13; - this.m20 = sourceMatrix.m20; - this.m21 = sourceMatrix.m21; - this.m22 = sourceMatrix.m22; - this.m23 = sourceMatrix.m23; - this.m30 = sourceMatrix.m30; - this.m31 = sourceMatrix.m31; - this.m32 = sourceMatrix.m32; - this.m33 = sourceMatrix.m33; - } - - /* Quaternions are not currently needed/implemented - public Matrix4float(Quaternion p_i48104_1_) - { - float f = p_i48104_1_.i(); - float f1 = p_i48104_1_.j(); - float f2 = p_i48104_1_.k(); - float f3 = p_i48104_1_.r(); - float f4 = 2.0F * f * f; - float f5 = 2.0F * f1 * f1; - float f6 = 2.0F * f2 * f2; - this.m00 = 1.0F - f5 - f6; - this.m11 = 1.0F - f6 - f4; - this.m22 = 1.0F - f4 - f5; - this.m33 = 1.0F; - float f7 = f * f1; - float f8 = f1 * f2; - float f9 = f2 * f; - float f10 = f * f3; - float f11 = f1 * f3; - float f12 = f2 * f3; - this.m10 = 2.0F * (f7 + f12); - this.m01 = 2.0F * (f7 - f12); - this.m20 = 2.0F * (f9 - f11); - this.m02 = 2.0F * (f9 + f11); - this.m21 = 2.0F * (f8 + f10); - this.m12 = 2.0F * (f8 - f10); - } - */ - - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - Mat4f otherMatrix = (Mat4f) obj; - return Float.compare(otherMatrix.m00, this.m00) == 0 - && Float.compare(otherMatrix.m01, this.m01) == 0 - && Float.compare(otherMatrix.m02, this.m02) == 0 - && Float.compare(otherMatrix.m03, this.m03) == 0 - && Float.compare(otherMatrix.m10, this.m10) == 0 - && Float.compare(otherMatrix.m11, this.m11) == 0 - && Float.compare(otherMatrix.m12, this.m12) == 0 - && Float.compare(otherMatrix.m13, this.m13) == 0 - && Float.compare(otherMatrix.m20, this.m20) == 0 - && Float.compare(otherMatrix.m21, this.m21) == 0 - && Float.compare(otherMatrix.m22, this.m22) == 0 - && Float.compare(otherMatrix.m23, this.m23) == 0 - && Float.compare(otherMatrix.m30, this.m30) == 0 - && Float.compare(otherMatrix.m31, this.m31) == 0 - && Float.compare(otherMatrix.m32, this.m32) == 0 - && Float.compare(otherMatrix.m33, this.m33) == 0; - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0; - i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0); - i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0); - i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0); - i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0); - i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0); - i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0); - i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0); - i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0); - i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0); - i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0); - i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0); - i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0); - i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0); - i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0); - return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0); - } - - - @Override - public String toString() - { - return "Matrix4f:\n" + - this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" + - this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" + - this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" + - this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n"; - } - - - public void store(FloatBuffer floatBuffer) - { - floatBuffer.put(bufferIndex(0, 0), this.m00); - floatBuffer.put(bufferIndex(0, 1), this.m01); - floatBuffer.put(bufferIndex(0, 2), this.m02); - floatBuffer.put(bufferIndex(0, 3), this.m03); - floatBuffer.put(bufferIndex(1, 0), this.m10); - floatBuffer.put(bufferIndex(1, 1), this.m11); - floatBuffer.put(bufferIndex(1, 2), this.m12); - floatBuffer.put(bufferIndex(1, 3), this.m13); - floatBuffer.put(bufferIndex(2, 0), this.m20); - floatBuffer.put(bufferIndex(2, 1), this.m21); - floatBuffer.put(bufferIndex(2, 2), this.m22); - floatBuffer.put(bufferIndex(2, 3), this.m23); - floatBuffer.put(bufferIndex(3, 0), this.m30); - floatBuffer.put(bufferIndex(3, 1), this.m31); - floatBuffer.put(bufferIndex(3, 2), this.m32); - floatBuffer.put(bufferIndex(3, 3), this.m33); - } - - private static int bufferIndex(int xIndex, int zIndex) - { - return (zIndex * 4) + xIndex; - } - - - public void setIdentity() - { - this.m00 = 1.0F; - this.m01 = 0.0F; - this.m02 = 0.0F; - this.m03 = 0.0F; - this.m10 = 0.0F; - this.m11 = 1.0F; - this.m12 = 0.0F; - this.m13 = 0.0F; - this.m20 = 0.0F; - this.m21 = 0.0F; - this.m22 = 1.0F; - this.m23 = 0.0F; - this.m30 = 0.0F; - this.m31 = 0.0F; - this.m32 = 0.0F; - this.m33 = 1.0F; - } - - /** adjugate and determinate */ - public float adjugateAndDet() - { - float f = this.m00 * this.m11 - this.m01 * this.m10; - float f1 = this.m00 * this.m12 - this.m02 * this.m10; - float f2 = this.m00 * this.m13 - this.m03 * this.m10; - float f3 = this.m01 * this.m12 - this.m02 * this.m11; - float f4 = this.m01 * this.m13 - this.m03 * this.m11; - float f5 = this.m02 * this.m13 - this.m03 * this.m12; - float f6 = this.m20 * this.m31 - this.m21 * this.m30; - float f7 = this.m20 * this.m32 - this.m22 * this.m30; - float f8 = this.m20 * this.m33 - this.m23 * this.m30; - float f9 = this.m21 * this.m32 - this.m22 * this.m31; - float f10 = this.m21 * this.m33 - this.m23 * this.m31; - float f11 = this.m22 * this.m33 - this.m23 * this.m32; - float f12 = this.m11 * f11 - this.m12 * f10 + this.m13 * f9; - float f13 = -this.m10 * f11 + this.m12 * f8 - this.m13 * f7; - float f14 = this.m10 * f10 - this.m11 * f8 + this.m13 * f6; - float f15 = -this.m10 * f9 + this.m11 * f7 - this.m12 * f6; - float f16 = -this.m01 * f11 + this.m02 * f10 - this.m03 * f9; - float f17 = this.m00 * f11 - this.m02 * f8 + this.m03 * f7; - float f18 = -this.m00 * f10 + this.m01 * f8 - this.m03 * f6; - float f19 = this.m00 * f9 - this.m01 * f7 + this.m02 * f6; - float f20 = this.m31 * f5 - this.m32 * f4 + this.m33 * f3; - float f21 = -this.m30 * f5 + this.m32 * f2 - this.m33 * f1; - float f22 = this.m30 * f4 - this.m31 * f2 + this.m33 * f; - float f23 = -this.m30 * f3 + this.m31 * f1 - this.m32 * f; - float f24 = -this.m21 * f5 + this.m22 * f4 - this.m23 * f3; - float f25 = this.m20 * f5 - this.m22 * f2 + this.m23 * f1; - float f26 = -this.m20 * f4 + this.m21 * f2 - this.m23 * f; - float f27 = this.m20 * f3 - this.m21 * f1 + this.m22 * f; - this.m00 = f12; - this.m10 = f13; - this.m20 = f14; - this.m30 = f15; - this.m01 = f16; - this.m11 = f17; - this.m21 = f18; - this.m31 = f19; - this.m02 = f20; - this.m12 = f21; - this.m22 = f22; - this.m32 = f23; - this.m03 = f24; - this.m13 = f25; - this.m23 = f26; - this.m33 = f27; - return f * f11 - f1 * f10 + f2 * f9 + f3 * f8 - f4 * f7 + f5 * f6; - } - - public void transpose() - { - float f = this.m10; - this.m10 = this.m01; - this.m01 = f; - f = this.m20; - this.m20 = this.m02; - this.m02 = f; - f = this.m21; - this.m21 = this.m12; - this.m12 = f; - f = this.m30; - this.m30 = this.m03; - this.m03 = f; - f = this.m31; - this.m31 = this.m13; - this.m13 = f; - f = this.m32; - this.m32 = this.m23; - this.m23 = f; - } - - public boolean invert() - { - float det = this.adjugateAndDet(); - if (Math.abs(det) > 1.0E-6F) - { - this.multiply(det); - return true; - } - else - { - return false; - } - } - - public void multiply(Mat4f multMatrix) - { - float f = this.m00 * multMatrix.m00 + this.m01 * multMatrix.m10 + this.m02 * multMatrix.m20 + this.m03 * multMatrix.m30; - float f1 = this.m00 * multMatrix.m01 + this.m01 * multMatrix.m11 + this.m02 * multMatrix.m21 + this.m03 * multMatrix.m31; - float f2 = this.m00 * multMatrix.m02 + this.m01 * multMatrix.m12 + this.m02 * multMatrix.m22 + this.m03 * multMatrix.m32; - float f3 = this.m00 * multMatrix.m03 + this.m01 * multMatrix.m13 + this.m02 * multMatrix.m23 + this.m03 * multMatrix.m33; - float f4 = this.m10 * multMatrix.m00 + this.m11 * multMatrix.m10 + this.m12 * multMatrix.m20 + this.m13 * multMatrix.m30; - float f5 = this.m10 * multMatrix.m01 + this.m11 * multMatrix.m11 + this.m12 * multMatrix.m21 + this.m13 * multMatrix.m31; - float f6 = this.m10 * multMatrix.m02 + this.m11 * multMatrix.m12 + this.m12 * multMatrix.m22 + this.m13 * multMatrix.m32; - float f7 = this.m10 * multMatrix.m03 + this.m11 * multMatrix.m13 + this.m12 * multMatrix.m23 + this.m13 * multMatrix.m33; - float f8 = this.m20 * multMatrix.m00 + this.m21 * multMatrix.m10 + this.m22 * multMatrix.m20 + this.m23 * multMatrix.m30; - float f9 = this.m20 * multMatrix.m01 + this.m21 * multMatrix.m11 + this.m22 * multMatrix.m21 + this.m23 * multMatrix.m31; - float f10 = this.m20 * multMatrix.m02 + this.m21 * multMatrix.m12 + this.m22 * multMatrix.m22 + this.m23 * multMatrix.m32; - float f11 = this.m20 * multMatrix.m03 + this.m21 * multMatrix.m13 + this.m22 * multMatrix.m23 + this.m23 * multMatrix.m33; - float f12 = this.m30 * multMatrix.m00 + this.m31 * multMatrix.m10 + this.m32 * multMatrix.m20 + this.m33 * multMatrix.m30; - float f13 = this.m30 * multMatrix.m01 + this.m31 * multMatrix.m11 + this.m32 * multMatrix.m21 + this.m33 * multMatrix.m31; - float f14 = this.m30 * multMatrix.m02 + this.m31 * multMatrix.m12 + this.m32 * multMatrix.m22 + this.m33 * multMatrix.m32; - float f15 = this.m30 * multMatrix.m03 + this.m31 * multMatrix.m13 + this.m32 * multMatrix.m23 + this.m33 * multMatrix.m33; - this.m00 = f; - this.m01 = f1; - this.m02 = f2; - this.m03 = f3; - this.m10 = f4; - this.m11 = f5; - this.m12 = f6; - this.m13 = f7; - this.m20 = f8; - this.m21 = f9; - this.m22 = f10; - this.m23 = f11; - this.m30 = f12; - this.m31 = f13; - this.m32 = f14; - this.m33 = f15; - } - - /* Quaternions aren't currently needed/implemented - public void multiply(Quaternion p_226596_1_) - { - this.multiply(new Matrix4f(p_226596_1_)); - } - */ - - public void multiply(float scalar) - { - this.m00 *= scalar; - this.m01 *= scalar; - this.m02 *= scalar; - this.m03 *= scalar; - this.m10 *= scalar; - this.m11 *= scalar; - this.m12 *= scalar; - this.m13 *= scalar; - this.m20 *= scalar; - this.m21 *= scalar; - this.m22 *= scalar; - this.m23 *= scalar; - this.m30 *= scalar; - this.m31 *= scalar; - this.m32 *= scalar; - this.m33 *= scalar; - } - - public static Mat4f perspective(double fov, float widthHeightRatio, float nearClipPlane, float farClipPlane) - { - float f = (float) (1.0D / Math.tan(fov * ((float) Math.PI / 180F) / 2.0D)); - Mat4f matrix = new Mat4f(); - matrix.m00 = f / widthHeightRatio; - matrix.m11 = f; - matrix.m22 = (farClipPlane + nearClipPlane) / (nearClipPlane - farClipPlane); - matrix.m32 = -1.0F; - matrix.m23 = 2.0F * farClipPlane * nearClipPlane / (nearClipPlane - farClipPlane); - return matrix; - } - - - /* not currently needed/implemented - * Also the parameter names should be double checked as they may be incorrect - public static Matrix4Float orthographic(float left, float right, float top, float bottom) - { - Matrix4Float matrix4f = new Matrix4Float(); - matrix4f.m00 = 2.0F / left; - matrix4f.m11 = 2.0F / right; - float f = bottom - top; - matrix4f.m22 = -2.0F / f; - matrix4f.m33 = 1.0F; - matrix4f.m03 = -1.0F; - matrix4f.m13 = -1.0F; - matrix4f.m23 = -(bottom + top) / f; - return matrix4f; - } - */ - - /** - * TODO: what kind of translation is this? - * and how is this different from "multiplyTranslationMatrix"? - */ - public void translate(Vec3f vec) - { - this.m03 += vec.x; - this.m13 += vec.y; - this.m23 += vec.z; - } - - /** originally "translate" from Minecraft's MatrixStack */ - public void multiplyTranslationMatrix(double x, double y, double z) - { - multiply(createTranslateMatrix((float)x, (float)y, (float)z)); - } - - public Mat4f copy() - { - return new Mat4f(this); - } - - public static Mat4f createScaleMatrix(float x, float y, float z) - { - Mat4f matrix = new Mat4f(); - matrix.m00 = x; - matrix.m11 = y; - matrix.m22 = z; - matrix.m33 = 1.0F; - return matrix; - } - - public static Mat4f createTranslateMatrix(float x, float y, float z) - { - Mat4f matrix = new Mat4f(); - matrix.m00 = 1.0F; - matrix.m11 = 1.0F; - matrix.m22 = 1.0F; - matrix.m33 = 1.0F; - matrix.m03 = x; - matrix.m13 = y; - matrix.m23 = z; - return matrix; - } - - - // Forge start - public Mat4f(float[] values) - { - m00 = values[0]; - m01 = values[1]; - m02 = values[2]; - m03 = values[3]; - m10 = values[4]; - m11 = values[5]; - m12 = values[6]; - m13 = values[7]; - m20 = values[8]; - m21 = values[9]; - m22 = values[10]; - m23 = values[11]; - m30 = values[12]; - m31 = values[13]; - m32 = values[14]; - m33 = values[15]; - } - - public Mat4f(FloatBuffer buffer) - { - this(buffer.array()); - } - - public void set(Mat4f mat) - { - this.m00 = mat.m00; - this.m01 = mat.m01; - this.m02 = mat.m02; - this.m03 = mat.m03; - this.m10 = mat.m10; - this.m11 = mat.m11; - this.m12 = mat.m12; - this.m13 = mat.m13; - this.m20 = mat.m20; - this.m21 = mat.m21; - this.m22 = mat.m22; - this.m23 = mat.m23; - this.m30 = mat.m30; - this.m31 = mat.m31; - this.m32 = mat.m32; - this.m33 = mat.m33; - } - - public void add(Mat4f other) - { - m00 += other.m00; - m01 += other.m01; - m02 += other.m02; - m03 += other.m03; - m10 += other.m10; - m11 += other.m11; - m12 += other.m12; - m13 += other.m13; - m20 += other.m20; - m21 += other.m21; - m22 += other.m22; - m23 += other.m23; - m30 += other.m30; - m31 += other.m31; - m32 += other.m32; - m33 += other.m33; - } - - public void multiplyBackward(Mat4f other) - { - Mat4f copy = other.copy(); - copy.multiply(this); - this.set(copy); - } - - public void setTranslation(float x, float y, float z) - { - this.m00 = 1.0F; - this.m11 = 1.0F; - this.m22 = 1.0F; - this.m33 = 1.0F; - this.m03 = x; - this.m13 = y; - this.m23 = z; - } - - /** - * Changes the values that store the clipping planes. - * Formula for calculating matrix values is the same that OpenGL uses when making matrices. - * - * @param nearClip New near clipping plane value. - * @param farClip New far clipping plane value. - */ - public void setClipPlanes(float nearClip,float farClip) - { - //convert to matrix values, formula copied from a textbook / openGL specification. - float matNearClip = -((farClip + nearClip) / (farClip - nearClip)); - float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip)); - //set new values for the clip planes. - this.m22 = matNearClip; - this.m23 = matFarClip; - } -} diff --git a/src/main/java/com/seibel/lod/core/objects/math/Vec3d.java b/src/main/java/com/seibel/lod/core/objects/math/Vec3d.java deleted file mode 100644 index c2ba8aa24..000000000 --- a/src/main/java/com/seibel/lod/core/objects/math/Vec3d.java +++ /dev/null @@ -1,257 +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.core.objects.math; - -import com.seibel.lod.core.util.LodUtil; - -/** - * This is closer to MC's implementation of a - * 3 element float vector than a 3 element double - * vector. Hopefully that shouldn't cause any issues. - * - * @author James Seibel - * @version 11-18-2021 - */ -public class Vec3d -{ - public static Vec3d XNeg = new Vec3d(-1.0F, 0.0F, 0.0F); - public static Vec3d XPos = new Vec3d(1.0F, 0.0F, 0.0F); - public static Vec3d YNeg = new Vec3d(0.0F, -1.0F, 0.0F); - public static Vec3d YPos = new Vec3d(0.0F, 1.0F, 0.0F); - public static Vec3d ZNeg = new Vec3d(0.0F, 0.0F, -1.0F); - public static Vec3d ZPos = new Vec3d(0.0F, 0.0F, 1.0F); - - public static final Vec3d ZERO_VECTOR = new Vec3d(0.0D, 0.0D, 0.0D); - - public double x; - public double y; - public double z; - - - - public Vec3d() - { - - } - - public Vec3d(double x, double y, double z) - { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - Vec3d Vec3f = (Vec3d) obj; - if (Double.compare(Vec3f.x, this.x) != 0) - { - return false; - } - else if (Double.compare(Vec3f.y, this.y) != 0) - { - return false; - } - else - { - return Double.compare(Vec3f.z, this.z) == 0; - } - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - long longVal = Double.doubleToLongBits(this.x); - - int intVal = (int) (longVal ^ longVal >>> 32); - longVal = Double.doubleToLongBits(this.y); - intVal = 31 * intVal + (int) (longVal ^ longVal >>> 32); - longVal = Double.doubleToLongBits(this.z); - - return 31 * intVal + (int) (longVal ^ longVal >>> 32); - } - - public void mul(double scalar) - { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - } - - public void mul(double x, double y, double z) - { - this.x *= x; - this.y *= y; - this.z *= z; - } - - public void clamp(double min, double max) - { - this.x = LodUtil.clamp(min, this.x, max); - this.y = LodUtil.clamp(min, this.y, max); - this.z = LodUtil.clamp(min, this.z, max); - } - - public void set(double x, double y, double z) - { - this.x = x; - this.y = y; - this.z = z; - } - - public void add(double x, double y, double z) - { - this.x += x; - this.y += y; - this.z += z; - } - - public void add(Vec3d vector) - { - this.x += vector.x; - this.y += vector.y; - this.z += vector.z; - } - - public void subtract(Vec3d vector) - { - this.x -= vector.x; - this.y -= vector.y; - this.z -= vector.z; - } - - public double dotProduct(Vec3d vector) - { - return this.x * vector.x + this.y * vector.y + this.z * vector.z; - } - - public Vec3d normalize() - { - double value = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); - return value < 1.0E-4D ? ZERO_VECTOR : new Vec3d(this.x / value, this.y / value, this.z / value); - } - - public void crossProduct(Vec3d vector) - { - double f = this.x; - double f1 = this.y; - double f2 = this.z; - double f3 = vector.x; - double f4 = vector.y; - double f5 = vector.z; - this.x = f1 * f5 - f2 * f4; - this.y = f2 * f3 - f * f5; - this.z = f * f4 - f1 * f3; - } - - /* Matrix3f is not currently needed/implemented - public void transform(Matrix3f p_229188_1_) - { - double f = this.x; - double f1 = this.y; - double f2 = this.z; - this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2; - this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2; - this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2; - } - */ - - /* Quaternions are not currently needed/implemented - public void transform(Quaternion p_214905_1_) - { - Quaternion quaternion = new Quaternion(p_214905_1_); - quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F)); - Quaternion quaternion1 = new Quaternion(p_214905_1_); - quaternion1.conj(); - quaternion.mul(quaternion1); - this.set(quaternion.i(), quaternion.j(), quaternion.k()); - } - */ - - /* not currently needed - * percent may actually be partial ticks (which is available when rendering) - public void linearInterp(Vec3f resultingVector, double percent) - { - double f = 1.0F - percent; - this.x = this.x * f + resultingVector.x * percent; - this.y = this.y * f + resultingVector.y * percent; - this.z = this.z * f + resultingVector.z * percent; - } - */ - - /* Quaternions are not currently needed/implemented - public Quaternion rotation(double p_229193_1_) - { - return new Quaternion(this, p_229193_1_, false); - } - - - @OnlyIn(Dist.CLIENT) - public Quaternion rotationDegrees(double p_229187_1_) - { - return new Quaternion(this, p_229187_1_, true); - } - */ - - public Vec3d copy() - { - return new Vec3d(this.x, this.y, this.z); - } - - /* not currently needed/implemented - public void map(double2doubleFunction p_229191_1_) - { - this.x = p_229191_1_.get(this.x); - this.y = p_229191_1_.get(this.y); - this.z = p_229191_1_.get(this.z); - } - */ - - @Override - public String toString() - { - return "[" + this.x + ", " + this.y + ", " + this.z + "]"; - } - - // Forge start - public Vec3d(double[] values) - { - set(values); - } - - public void set(double[] values) - { - this.x = values[0]; - this.y = values[1]; - this.z = values[2]; - } -} diff --git a/src/main/java/com/seibel/lod/core/objects/math/Vec3f.java b/src/main/java/com/seibel/lod/core/objects/math/Vec3f.java deleted file mode 100644 index fe25d8889..000000000 --- a/src/main/java/com/seibel/lod/core/objects/math/Vec3f.java +++ /dev/null @@ -1,261 +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.core.objects.math; - -import com.seibel.lod.core.util.LodUtil; - -/** - * A (almost) exact copy of Minecraft's 1.16.5 - * implementation of a 3 element float vector. - * - * @author James Seibel - * @version 11-11-2021 - */ -public class Vec3f -{ - public static Vec3f XNeg = new Vec3f(-1.0F, 0.0F, 0.0F); - public static Vec3f XPos = new Vec3f(1.0F, 0.0F, 0.0F); - public static Vec3f YNeg = new Vec3f(0.0F, -1.0F, 0.0F); - public static Vec3f YPos = new Vec3f(0.0F, 1.0F, 0.0F); - public static Vec3f ZNeg = new Vec3f(0.0F, 0.0F, -1.0F); - public static Vec3f ZPos = new Vec3f(0.0F, 0.0F, 1.0F); - - - public float x; - public float y; - public float z; - - - - public Vec3f() - { - - } - - public Vec3f(float x, float y, float z) - { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - Vec3f Vec3f = (Vec3f) obj; - if (Float.compare(Vec3f.x, this.x) != 0) - { - return false; - } - else if (Float.compare(Vec3f.y, this.y) != 0) - { - return false; - } - else - { - return Float.compare(Vec3f.z, this.z) == 0; - } - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - int i = Float.floatToIntBits(this.x); - i = 31 * i + Float.floatToIntBits(this.y); - return 31 * i + Float.floatToIntBits(this.z); - } - - public void mul(float scalar) - { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - } - - public void mul(float x, float y, float z) - { - this.x *= x; - this.y *= y; - this.z *= z; - } - - public void clamp(float min, float max) - { - this.x = LodUtil.clamp(min, this.x, max); - this.y = LodUtil.clamp(min, this.y, max); - this.z = LodUtil.clamp(min, this.z, max); - } - - public void set(float x, float y, float z) - { - this.x = x; - this.y = y; - this.z = z; - } - - public void add(float x, float y, float z) - { - this.x += x; - this.y += y; - this.z += z; - } - - public void add(Vec3f vector) - { - this.x += vector.x; - this.y += vector.y; - this.z += vector.z; - } - - public void subtract(Vec3f vector) - { - this.x -= vector.x; - this.y -= vector.y; - this.z -= vector.z; - } - - public float dotProduct(Vec3f vector) - { - return this.x * vector.x + this.y * vector.y + this.z * vector.z; - } - - public boolean normalize() - { - float squaredSum = this.x * this.x + this.y * this.y + this.z * this.z; - if (squaredSum < 1.0E-5D) - { - return false; - } - else - { - float f1 = LodUtil.fastInvSqrt(squaredSum); - this.x *= f1; - this.y *= f1; - this.z *= f1; - return true; - } - } - - public void crossProduct(Vec3f vector) - { - float f = this.x; - float f1 = this.y; - float f2 = this.z; - float f3 = vector.x; - float f4 = vector.y; - float f5 = vector.z; - this.x = f1 * f5 - f2 * f4; - this.y = f2 * f3 - f * f5; - this.z = f * f4 - f1 * f3; - } - - /* Matrix3f is not currently needed/implemented - public void transform(Matrix3f p_229188_1_) - { - float f = this.x; - float f1 = this.y; - float f2 = this.z; - this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2; - this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2; - this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2; - } - */ - - /* Quaternions are not currently needed/implemented - public void transform(Quaternion p_214905_1_) - { - Quaternion quaternion = new Quaternion(p_214905_1_); - quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F)); - Quaternion quaternion1 = new Quaternion(p_214905_1_); - quaternion1.conj(); - quaternion.mul(quaternion1); - this.set(quaternion.i(), quaternion.j(), quaternion.k()); - } - */ - - /* not currently needed - * percent may actually be partial ticks (which is available when rendering) - public void linearInterp(Vec3f resultingVector, float percent) - { - float f = 1.0F - percent; - this.x = this.x * f + resultingVector.x * percent; - this.y = this.y * f + resultingVector.y * percent; - this.z = this.z * f + resultingVector.z * percent; - } - */ - - /* Quaternions are not currently needed/implemented - public Quaternion rotation(float p_229193_1_) - { - return new Quaternion(this, p_229193_1_, false); - } - - - @OnlyIn(Dist.CLIENT) - public Quaternion rotationDegrees(float p_229187_1_) - { - return new Quaternion(this, p_229187_1_, true); - } - */ - - public Vec3f copy() - { - return new Vec3f(this.x, this.y, this.z); - } - - /* not currently needed/implemented - public void map(Float2FloatFunction p_229191_1_) - { - this.x = p_229191_1_.get(this.x); - this.y = p_229191_1_.get(this.y); - this.z = p_229191_1_.get(this.z); - } - */ - - @Override - public String toString() - { - return "[" + this.x + ", " + this.y + ", " + this.z + "]"; - } - - // Forge start - public Vec3f(float[] values) - { - set(values); - } - - public void set(float[] values) - { - this.x = values[0]; - this.y = values[1]; - this.z = values[2]; - } -} diff --git a/src/main/java/com/seibel/lod/core/objects/math/Vec3i.java b/src/main/java/com/seibel/lod/core/objects/math/Vec3i.java deleted file mode 100644 index a65a404f0..000000000 --- a/src/main/java/com/seibel/lod/core/objects/math/Vec3i.java +++ /dev/null @@ -1,203 +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.core.objects.math; - -import com.seibel.lod.core.util.LodUtil; - -/** - * A (almost) exact copy of Minecraft's 1.16.5 - * implementation of a 3 element integer vector. - * - * @author James Seibel - * @version 11-11-2021 - */ -public class Vec3i -{ - public static Vec3i XNeg = new Vec3i(-1, 0, 0); - public static Vec3i XPos = new Vec3i(1, 0, 0); - public static Vec3i YNeg = new Vec3i(0, -1, 0); - public static Vec3i YPos = new Vec3i(0, 1, 0); - public static Vec3i ZNeg = new Vec3i(0, 0, -1); - public static Vec3i ZPos = new Vec3i(0, 0, 1); - - - public int x; - public int y; - public int z; - - - - public Vec3i() - { - - } - - public Vec3i(int x, int y, int z) - { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - Vec3i Vec3f = (Vec3i) obj; - if (Float.compare(Vec3f.x, this.x) != 0) - { - return false; - } - else if (Float.compare(Vec3f.y, this.y) != 0) - { - return false; - } - else - { - return Float.compare(Vec3f.z, this.z) == 0; - } - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - int i = Float.floatToIntBits(this.x); - i = 31 * i + Float.floatToIntBits(this.y); - return 31 * i + Float.floatToIntBits(this.z); - } - - public void mul(float scalar) - { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - } - - public void mul(float x, float y, float z) - { - this.x *= x; - this.y *= y; - this.z *= z; - } - - public void clamp(int min, int max) - { - this.x = LodUtil.clamp(min, this.x, max); - this.y = LodUtil.clamp(min, this.y, max); - this.z = LodUtil.clamp(min, this.z, max); - } - - public void set(int x, int y, int z) - { - this.x = x; - this.y = y; - this.z = z; - } - - public void add(int x, int y, int z) - { - this.x += x; - this.y += y; - this.z += z; - } - - public void add(Vec3i vector) - { - this.x += vector.x; - this.y += vector.y; - this.z += vector.z; - } - - public void subtract(Vec3i vector) - { - this.x -= vector.x; - this.y -= vector.y; - this.z -= vector.z; - } - - public double distSqr(double x, double y, double z, boolean centerOfBlock) - { - double offset = centerOfBlock ? 0.5 : 0.0; - double xAdd = this.x + offset - x; - double yAdd = this.y + offset - y; - double zAdd = this.z + offset - z; - return (xAdd * xAdd) + (yAdd * yAdd) + (zAdd * zAdd); - } - - public int distManhattan(Vec3i otherVec) - { - float xSub = Math.abs(otherVec.x - this.x); - float ySub = Math.abs(otherVec.y - this.y); - float zSub = Math.abs(otherVec.z - this.z); - return (int) (xSub + ySub + zSub); - } - - /** inner product */ - public float dotProduct(Vec3i vector) - { - return (this.x * vector.x) + (this.y * vector.y) + (this.z * vector.z); - } - - /** Cross product */ - public Vec3i cross(Vec3i otherVec) - { - return new Vec3i( - (this.y * otherVec.z) - (this.z * otherVec.y), - (this.z * otherVec.x) - (this.x * otherVec.z), - (this.x * otherVec.y) - (this.y * otherVec.x)); - } - - public Vec3i copy() - { - return new Vec3i(this.x, this.y, this.z); - } - - - - @Override - public String toString() - { - return "[" + this.x + ", " + this.y + ", " + this.z + "]"; - } - - - // Forge start - public Vec3i(int[] values) - { - set(values); - } - - public void set(int[] values) - { - this.x = values[0]; - this.y = values[1]; - this.z = values[2]; - } -} diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/DefaultLodVertexFormats.java b/src/main/java/com/seibel/lod/core/objects/opengl/DefaultLodVertexFormats.java deleted file mode 100644 index 116ed0f0c..000000000 --- a/src/main/java/com/seibel/lod/core/objects/opengl/DefaultLodVertexFormats.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.core.objects.opengl; - -import com.google.common.collect.ImmutableList; - -/** - * A (almost) exact copy of MC's - * DefaultVertexFormats class. - * - * @author James Seibel - * @version 11-13-2021 - */ -public class DefaultLodVertexFormats -{ - public static final LodVertexFormatElement ELEMENT_POSITION = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 3); - public static final LodVertexFormatElement ELEMENT_COLOR = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 4); - public static final LodVertexFormatElement ELEMENT_UV = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 2); - public static final LodVertexFormatElement ELEMENT_LIGHT_MAP_UV = new LodVertexFormatElement(1, LodVertexFormatElement.DataType.SHORT, 2); - public static final LodVertexFormatElement ELEMENT_NORMAL = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 3); - public static final LodVertexFormatElement ELEMENT_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1); - - - public static final LodVertexFormat POSITION = new LodVertexFormat(ImmutableList.builder().add(ELEMENT_POSITION).build()); - public static final LodVertexFormat POSITION_COLOR = new LodVertexFormat(ImmutableList.builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).build()); - public static final LodVertexFormat POSITION_COLOR_LIGHTMAP = new LodVertexFormat(ImmutableList.builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_LIGHT_MAP_UV).build()); - public static final LodVertexFormat POSITION_TEX = new LodVertexFormat(ImmutableList.builder().add(ELEMENT_POSITION).add(ELEMENT_UV).build()); - public static final LodVertexFormat POSITION_COLOR_TEX = new LodVertexFormat(ImmutableList.builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).build()); - public static final LodVertexFormat POSITION_COLOR_TEX_LIGHTMAP = new LodVertexFormat(ImmutableList.builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).add(ELEMENT_LIGHT_MAP_UV).build()); - -} diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/LodBufferBuilder.java b/src/main/java/com/seibel/lod/core/objects/opengl/LodBufferBuilder.java deleted file mode 100644 index 29d764092..000000000 --- a/src/main/java/com/seibel/lod/core/objects/opengl/LodBufferBuilder.java +++ /dev/null @@ -1,543 +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.core.objects.opengl; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; - -/** - * A (almost) exact copy of Minecraft's - * BufferBuilder object.
- * Which allows for creating and filling - * OpenGL buffers. - * - * @author James Seibel - * @version 11-13-2021 - */ -public class LodBufferBuilder -{ - private static final Logger LOGGER = LogManager.getLogger(); - public ByteBuffer buffer; - - private final List vertexCounts = Lists.newArrayList(); - private int lastRenderedCountIndex = 0; - private int totalRenderedBytes = 0; - private int nextElementByte = 0; - private int totalUploadedBytes = 0; - private int vertices; - private LodVertexFormatElement currentElement; - private int elementIndex; - private int mode; - private LodVertexFormat format; - private boolean building; - - - - - public LodBufferBuilder(int bufferSizeInBytes) - { - this.buffer = allocateByteBuffer(bufferSizeInBytes * 4); - } - - - - /** originally from MC's GLAllocation class */ - private ByteBuffer allocateByteBuffer(int bufferSizeInBytes) - { - return ByteBuffer.allocateDirect(bufferSizeInBytes).order(ByteOrder.nativeOrder()); - } - /** originally from MC's GLAllocation class */ - @SuppressWarnings("unused") - private FloatBuffer allocateFloatBuffer(int bufferSizeInBytes) - { - return allocateByteBuffer(bufferSizeInBytes).asFloatBuffer(); - } - - - - /** make sure the buffer doesn't overflow when inserting new elements */ - private void ensureVertexCapacity() - { - this.ensureCapacity(this.format.getVertexSize()); - } - private void ensureCapacity(int vertexSizeInBytes) - { - if (this.nextElementByte + vertexSizeInBytes > this.buffer.capacity()) - { - int i = this.buffer.capacity(); - int j = i + roundUp(vertexSizeInBytes); - //LOGGER.debug("Needed to grow BufferBuilder buffer: Old size {} bytes, new size {} bytes.", i, j); - ByteBuffer bytebuffer = allocateByteBuffer(j); - this.buffer.position(0); - bytebuffer.put(this.buffer); - bytebuffer.rewind(); - this.buffer = bytebuffer; - } - } - private static int roundUp(int vertexSizeInBytes) - { - int i = 2097152; // 2 ^ 21 - if (vertexSizeInBytes == 0) - { - return i; - } - else - { - if (vertexSizeInBytes < 0) - { - i *= -1; - } - - int j = vertexSizeInBytes % i; - return j == 0 ? vertexSizeInBytes : vertexSizeInBytes + i - j; - } - } - - - /* not currently needed sortQuads() - // the x,y,z location is a blockPos - public void sortQuads(float x, float y, float z) - { - ((Buffer) this.buffer).clear(); - FloatBuffer floatbuffer = this.buffer.asFloatBuffer(); - int i = this.vertices / 4; - float[] afloat = new float[i]; - - for (int j = 0; j < i; ++j) - { - afloat[j] = getQuadDistanceFromPlayer(floatbuffer, x, y, z, this.format.getIntegerSize(), this.totalRenderedBytes / 4 + j * this.format.getVertexSize()); - } - - int[] aint = new int[i]; - - for (int k = 0; k < aint.length; aint[k] = k++) - { - } - - IntArrays.mergeSort(aint, (p_227830_1_, p_227830_2_) -> - { - return Floats.compare(afloat[p_227830_2_], afloat[p_227830_1_]); - }); - BitSet bitset = new BitSet(); - FloatBuffer floatbuffer1 = allocateFloatBuffer(this.format.getIntegerSize() * 4); - - for (int l = bitset.nextClearBit(0); l < aint.length; l = bitset.nextClearBit(l + 1)) - { - int i1 = aint[l]; - if (i1 != l) - { - this.limitToVertex(floatbuffer, i1); - ((Buffer) floatbuffer1).clear(); - floatbuffer1.put(floatbuffer); - int j1 = i1; - - for (int k1 = aint[i1]; j1 != l; k1 = aint[k1]) - { - this.limitToVertex(floatbuffer, k1); - FloatBuffer floatbuffer2 = floatbuffer.slice(); - this.limitToVertex(floatbuffer, j1); - floatbuffer.put(floatbuffer2); - bitset.set(j1); - j1 = k1; - } - - this.limitToVertex(floatbuffer, l); - ((Buffer) floatbuffer1).flip(); - floatbuffer.put(floatbuffer1); - } - - bitset.set(l); - } - } - - - private void limitToVertex(FloatBuffer p_227829_1_, int p_227829_2_) - { - int i = this.format.getIntegerSize() * 4; - ((Buffer) p_227829_1_).limit(this.totalRenderedBytes / 4 + (p_227829_2_ + 1) * i); - ((Buffer) p_227829_1_).position(this.totalRenderedBytes / 4 + p_227829_2_ * i); - } - */ - - /* not curerntly needed getState() - public LodBufferBuilder.State getState() - { - ((Buffer) this.buffer).limit(this.nextElementByte); - ((Buffer) this.buffer).position(this.totalRenderedBytes); - ByteBuffer bytebuffer = ByteBuffer.allocate(this.vertices * this.format.getVertexSize()); - bytebuffer.put(this.buffer); - ((Buffer) this.buffer).clear(); - return new LodBufferBuilder.State(bytebuffer, this.format); - } - */ - - /* not currently needed getQuadDistanceFromPlayer() - private static float getQuadDistanceFromPlayer(FloatBuffer floatBuffer, float x, float y, float z, int p_181665_4_, int p_181665_5_) - { - float f = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 0); - float f1 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 1); - float f2 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 2); - float f3 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 0); - float f4 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 1); - float f5 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 2); - float f6 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 0); - float f7 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 1); - float f8 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 2); - float f9 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 0); - float f10 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 1); - float f11 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 2); - float f12 = (f + f3 + f6 + f9) * 0.25F - x; - float f13 = (f1 + f4 + f7 + f10) * 0.25F - y; - float f14 = (f2 + f5 + f8 + f11) * 0.25F - z; - return f12 * f12 + f13 * f13 + f14 * f14; - } - */ - - /* not currently needed restoreState() - public void restoreState(LodBufferBuilder.State bufferState) - { - ((Buffer) bufferState.data).clear(); - int i = bufferState.data.capacity(); - this.ensureCapacity(i); - ((Buffer) this.buffer).limit(this.buffer.capacity()); - ((Buffer) this.buffer).position(this.totalRenderedBytes); - this.buffer.put(bufferState.data); - ((Buffer) this.buffer).clear(); - LodVertexFormat LodVertexFormat = bufferState.format; - this.switchFormat(LodVertexFormat); - this.vertices = i / LodVertexFormat.getVertexSize(); - this.nextElementByte = this.totalRenderedBytes + this.vertices * LodVertexFormat.getVertexSize(); - } - */ - - - - - - private void switchFormat(LodVertexFormat newFormat) - { - format = newFormat; - } - - - - - //========================================// - // methods for actually building a buffer // - //========================================// - - /** - * @param openGlLodVertexFormat GL11.GL_QUADS, GL11.GL_TRIANGLES, etc. - * @param LodVertexFormat - */ - public void begin(int openGlLodVertexFormat, LodVertexFormat LodVertexFormat) - { - if (this.building) - { - throw new IllegalStateException("Already building!"); - } - else - { - this.building = true; - this.mode = openGlLodVertexFormat; - this.switchFormat(LodVertexFormat); - this.currentElement = LodVertexFormat.getElements().get(0); - this.elementIndex = 0; - this.buffer.clear(); - } - } - - public void end() - { - if (!this.building) - { - throw new IllegalStateException("Not building!"); - } - else - { - this.building = false; - this.vertexCounts.add(new LodBufferBuilder.DrawState(this.format, this.vertices, this.mode)); - this.totalRenderedBytes += this.vertices * this.format.getVertexSize(); - this.vertices = 0; - this.currentElement = null; - this.elementIndex = 0; - } - } - - public void putByte(int index, byte newByte) - { - this.buffer.put(this.nextElementByte + index, newByte); - } - - public void putShort(int index, short newShort) - { - this.buffer.putShort(this.nextElementByte + index, newShort); - } - - public void putFloat(int index, float newFloat) - { - this.buffer.putFloat(this.nextElementByte + index, newFloat); - } - - public void endVertex() - { - if (this.elementIndex != 0) - { - throw new IllegalStateException("Not filled all elements of the vertex"); - } - else - { - ++this.vertices; - this.ensureVertexCapacity(); - } - } - - public void nextElement() - { - ImmutableList immutablelist = this.format.getElements(); - this.elementIndex = (this.elementIndex + 1) % immutablelist.size(); - this.nextElementByte += this.currentElement.getByteSize(); - this.currentElement = immutablelist.get(this.elementIndex); -// if (LodVertexFormatelement.getUsage() == LodVertexFormatElement.Usage.PADDING) -// { -// this.nextElement(); -// } - -// if (this.defaultColorSet && this.currentElement.getUsage() == LodVertexFormatElement.Usage.COLOR) -// { -// color(this.defaultR, this.defaultG, this.defaultB, this.defaultA); -// } - - } - - public LodBufferBuilder color(int red, int green, int blue, int alpha) - { - LodVertexFormatElement LodVertexFormatelement = this.currentElement(); - if (LodVertexFormatelement.getType() != LodVertexFormatElement.DataType.UBYTE) - { - throw new IllegalStateException("Color must be stored as a UBYTE"); - } - else - { - this.putByte(0, (byte) red); - this.putByte(1, (byte) green); - this.putByte(2, (byte) blue); - this.putByte(3, (byte) alpha); - this.nextElement(); - return this; - } - } - - public LodBufferBuilder vertex(float x, float y, float z) - { - if (this.currentElement().getType() != LodVertexFormatElement.DataType.FLOAT) - { - throw new IllegalStateException("Position verticies must be stored as a FLOAT"); - } - else - { - this.putFloat(0, x); - this.putFloat(4, y); - this.putFloat(8, z); - this.nextElement(); - return this; - } - } - - - - - - - /* not currently needed fullVertex() - * TODO James isn't sure about these names - public void vertex(float blockPosX, float blockPosY, float blockPosZ, - float red, float green, float blue, float alpha, - float textureU, float textureV, - int p_225588_10_, int p_225588_11_, - float p_225588_12_, float p_225588_13_, float p_225588_14_) - { - if (this.defaultColorSet) - { - throw new IllegalStateException(); - } - else if (this.fastFormat) - { - this.putFloat(0, blockPosX); - this.putFloat(4, blockPosY); - this.putFloat(8, blockPosZ); - this.putByte(12, (byte) ((int) (red * 255.0F))); - this.putByte(13, (byte) ((int) (green * 255.0F))); - this.putByte(14, (byte) ((int) (blue * 255.0F))); - this.putByte(15, (byte) ((int) (alpha * 255.0F))); - this.putFloat(16, textureU); - this.putFloat(20, textureV); - int i; - if (this.fullFormat) - { - this.putShort(24, (short) (p_225588_10_ & '\uffff')); - this.putShort(26, (short) (p_225588_10_ >> 16 & '\uffff')); - i = 28; - } - else - { - i = 24; - } - - this.putShort(i + 0, (short) (p_225588_11_ & '\uffff')); - this.putShort(i + 2, (short) (p_225588_11_ >> 16 & '\uffff')); - this.putByte(i + 4, IVertexConsumer.normalIntValue(p_225588_12_)); - this.putByte(i + 5, IVertexConsumer.normalIntValue(p_225588_13_)); - this.putByte(i + 6, IVertexConsumer.normalIntValue(p_225588_14_)); - this.nextElementByte += i + 8; - this.endVertex(); - } - else - { - super.vertex(blockPosX, blockPosY, blockPosZ, red, green, blue, alpha, textureU, textureV, p_225588_10_, p_225588_11_, p_225588_12_, p_225588_13_, p_225588_14_); - } - } - */ - - /** - * James isn't sure what the difference between - * using this method and just directly getting the buffer would be. - * But this was what was being used before, so it will stay for now. - * - * If anyone figures out what is special about this, please replace this comment. - */ - public ByteBuffer getCleanedByteBuffer() - { - LodBufferBuilder.DrawState bufferbuilder$drawstate = this.vertexCounts.get(this.lastRenderedCountIndex++); - this.buffer.position(this.totalUploadedBytes); - this.totalUploadedBytes += bufferbuilder$drawstate.vertexCount() * bufferbuilder$drawstate.format().getVertexSize(); - this.buffer.limit(this.totalUploadedBytes); - if (this.lastRenderedCountIndex == this.vertexCounts.size() && this.vertices == 0) - { - this.clear(); - } - - ByteBuffer bytebuffer = this.buffer.slice(); - bytebuffer.order(this.buffer.order()); // FORGE: Fix incorrect byte order - this.buffer.clear(); - return bytebuffer; // the original method also returned bufferbuilder$drawstate - } - - - public void clear() - { - if (this.totalRenderedBytes != this.totalUploadedBytes) - { - LOGGER.warn("Bytes mismatch " + this.totalRenderedBytes + " " + this.totalUploadedBytes); - } - - this.discard(); - } - - public void discard() - { - this.totalRenderedBytes = 0; - this.totalUploadedBytes = 0; - this.nextElementByte = 0; - this.vertexCounts.clear(); - this.lastRenderedCountIndex = 0; - } - - public LodVertexFormatElement currentElement() - { - if (this.currentElement == null) - { - throw new IllegalStateException("BufferBuilder not started"); - } - else - { - return this.currentElement; - } - } - - public boolean building() - { - return this.building; - } - - - - - - //==================// - // internal classes // - //==================// - - - public static final class DrawState - { - private final LodVertexFormat format; - private final int vertexCount; - private final int mode; - - private DrawState(LodVertexFormat p_i225905_1_, int p_i225905_2_, int p_i225905_3_) - { - this.format = p_i225905_1_; - this.vertexCount = p_i225905_2_; - this.mode = p_i225905_3_; - } - - public LodVertexFormat format() - { - return this.format; - } - - public int vertexCount() - { - return this.vertexCount; - } - - public int mode() - { - return this.mode; - } - } - - - - // Forge added methods - public void putBulkData(ByteBuffer buffer) - { - ensureCapacity(buffer.limit() + this.format.getVertexSize()); - this.buffer.position(this.vertices * this.format.getVertexSize()); - this.buffer.put(buffer); - this.vertices += buffer.limit() / this.format.getVertexSize(); - this.nextElementByte += buffer.limit(); - } - - public LodVertexFormat getLodVertexFormat() - { - return this.format; - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java b/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java deleted file mode 100644 index a933e84ba..000000000 --- a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexBuffer.java +++ /dev/null @@ -1,57 +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.core.objects.opengl; - -import org.lwjgl.opengl.GL15; - -import com.seibel.lod.core.enums.rendering.GLProxyContext; -import com.seibel.lod.core.render.GLProxy; - -/** - * This is a container for a OpenGL - * VBO (Vertex Buffer Object). - * - * @author James Seibel - * @version 11-20-2021 - */ -public class LodVertexBuffer implements AutoCloseable -{ - public int id; - public int vertexCount; - - public LodVertexBuffer() - { - if (GLProxy.getInstance().getGlContext() == GLProxyContext.NONE) - throw new IllegalStateException("Thread [" +Thread.currentThread().getName() + "] tried to create a [" + LodVertexBuffer.class.getSimpleName() + "] outside a OpenGL contex."); - - this.id = GL15.glGenBuffers(); - } - - - @Override - public void close() - { - if (this.id >= 0) - { - GLProxy.getInstance().recordOpenGlCall(() -> GL15.glDeleteBuffers(this.id)); - this.id = -1; - } - } -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexFormat.java b/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexFormat.java deleted file mode 100644 index a35cee4e9..000000000 --- a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexFormat.java +++ /dev/null @@ -1,186 +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.core.objects.opengl; - -import java.util.stream.Collectors; - -import com.google.common.collect.ImmutableList; - -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; - -/** - * This is used to represent a single vertex - * stored in GPU memory, - *

- * A (almost) exact copy of Minecraft's - * VertexFormat class, several methods - * were commented out since we didn't need them. - * - * @author James Seibel - * @version 11-13-2021 - */ -public class LodVertexFormat -{ - private final ImmutableList elements; - private final IntList offsets = new IntArrayList(); - private final int vertexSize; - - public LodVertexFormat(ImmutableList elementList) - { - this.elements = elementList; - int i = 0; - - for (LodVertexFormatElement LodVertexFormatElement : elementList) - { - this.offsets.add(i); - i += LodVertexFormatElement.getByteSize(); - } - - this.vertexSize = i; - } - - public int getIntegerSize() - { - return this.getVertexSize() / 4; - } - - public int getVertexSize() - { - return this.vertexSize; - } - - public ImmutableList getElements() - { - return this.elements; - } - - - // Forge added method - public int getOffset(int index) - { - return offsets.getInt(index); - } - - - - @Override - public String toString() - { - return "format: " + this.elements.size() + " elements: " + this.elements.stream().map(Object::toString).collect(Collectors.joining(" ")); - } - - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - LodVertexFormat vertexformat = (LodVertexFormat) obj; - return this.vertexSize == vertexformat.vertexSize && this.elements.equals(vertexformat.elements); - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - return this.elements.hashCode(); - } - - - - - - - - /* not currently needed setupBufferState() - public void setupBufferState(long p_227892_1_) - { - if (!RenderSystem.isOnRenderThread()) - { - RenderSystem.recordRenderCall(() -> - { - this.setupBufferState(p_227892_1_); - }); - } - else - { - int i = this.getVertexSize(); - List list = this.getElements(); - - for (int j = 0; j < list.size(); ++j) - { - list.get(j).setupBufferState(p_227892_1_ + this.offsets.getInt(j), i); - } - - } - } - */ - - /* not currently needed clearBufferState() - public void clearBufferState() - { - if (!RenderSystem.isOnRenderThread()) - { - RenderSystem.recordRenderCall(this::clearBufferState); - } - else - { - for (LodVertexFormatElement LodVertexFormatElement : this.getElements()) - { - LodVertexFormatElement.clearBufferState(); - } - - } - } - */ - - - /* not currently needed has-Position/Normal/Color/UV - public boolean hasPosition() - { - return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.POSITION); - } - - public boolean hasNormal() - { - return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.NORMAL); - } - - public boolean hasColor() - { - return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.COLOR); - } - - public boolean hasUV(int which) - { - return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.UV && e.getIndex() == which); - } - */ - -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexFormatElement.java b/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexFormatElement.java deleted file mode 100644 index a85ca82cf..000000000 --- a/src/main/java/com/seibel/lod/core/objects/opengl/LodVertexFormatElement.java +++ /dev/null @@ -1,161 +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.core.objects.opengl; - -import org.lwjgl.opengl.GL11; - -/** - * This object is used to build LodVertexFormats. - *

- * A (almost) exact copy of Minecraft's - * VertexFormatElement class.
- * A number of things were removed from the original - * object since we didn't need them, specifically "usage". - * - * @author James Seibel - * @version 11-13-2021 - */ -public class LodVertexFormatElement -{ - private final LodVertexFormatElement.DataType dataType; - /** James isn't sure what index is for */ - private final int index; - private final int count; - private final int byteSize; - - public LodVertexFormatElement(int newIndex, LodVertexFormatElement.DataType newType, int newCount) - { - this.dataType = newType; - this.index = newIndex; - this.count = newCount; - this.byteSize = newType.getSize() * this.count; - } - - public final LodVertexFormatElement.DataType getType() - { - return this.dataType; - } - - public final int getIndex() - { - return this.index; - } - - public final int getByteSize() - { - return this.byteSize; - } - - // added by Forge - public int getElementCount() - { - return count; - } - - - - public enum DataType - { - FLOAT(4, "Float", GL11.GL_FLOAT), - UBYTE(1, "Unsigned Byte", GL11.GL_UNSIGNED_BYTE), - BYTE(1, "Byte", GL11.GL_BYTE), - USHORT(2, "Unsigned Short", GL11.GL_UNSIGNED_SHORT), - SHORT(2, "Short", GL11.GL_SHORT), - UINT(4, "Unsigned Int", GL11.GL_UNSIGNED_INT), - INT(4, "Int", GL11.GL_INT); - - private final int size; - private final String name; - private final int glType; - - DataType(int sizeInBytes, String newName, int openGlDataType) - { - this.size = sizeInBytes; - this.name = newName; - this.glType = openGlDataType; - } - - public int getSize() - { - return this.size; - } - - public String getName() - { - return this.name; - } - - public int getGlType() - { - return this.glType; - } - } - - - - - @Override - public int hashCode() - { - int i = this.dataType.hashCode(); - i = 31 * i + this.index; - return 31 * i + this.count; - } - - @Override - public String toString() - { - return this.count + "," + this.dataType.getName(); - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - LodVertexFormatElement LodVertexFormatElement = (LodVertexFormatElement) obj; - if (this.count != LodVertexFormatElement.count) - { - return false; - } - else if (this.index != LodVertexFormatElement.index) - { - return false; - } - else if (this.dataType != LodVertexFormatElement.dataType) - { - return false; - } - else - { - return false; - } - } - else - { - return false; - } - } - -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/objects/rending/LodFogConfig.java b/src/main/java/com/seibel/lod/core/objects/rending/LodFogConfig.java deleted file mode 100644 index 0fcdba0db..000000000 --- a/src/main/java/com/seibel/lod/core/objects/rending/LodFogConfig.java +++ /dev/null @@ -1,43 +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.core.objects.rending; - -import com.seibel.lod.core.enums.rendering.FogDistance; -import com.seibel.lod.core.enums.rendering.FogDrawMode; - -/** - * This object is just a replacement for an array - * to make things easier to understand in the LodRenderer. - * - * @author James Seibel - * @version 11-26-2021 - */ -public class LodFogConfig -{ - public FogDrawMode fogDrawMode; - public FogDistance fogDistance; - - - public float nearFogStart = 0; - public float nearFogEnd = 0; - - public float farFogStart = 0; - public float farFogEnd = 0; -} diff --git a/src/main/java/com/seibel/lod/core/render/GLProxy.java b/src/main/java/com/seibel/lod/core/render/GLProxy.java deleted file mode 100644 index 9843d3e4c..000000000 --- a/src/main/java/com/seibel/lod/core/render/GLProxy.java +++ /dev/null @@ -1,426 +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.core.render; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.lwjgl.glfw.GLFW; -import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; -import org.lwjgl.opengl.GL30; -import org.lwjgl.opengl.GLCapabilities; - -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.seibel.lod.core.ModInfo; -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.enums.config.GpuUploadMethod; -import com.seibel.lod.core.enums.rendering.GLProxyContext; -import com.seibel.lod.core.render.shader.LodShader; -import com.seibel.lod.core.render.shader.LodShaderProgram; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; - -/** - * 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://www.slideshare.net/CassEveritt/approaching-zero-driver-overhead

- * - * https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one
- * https://stackoverflow.com/questions/63509735/massive-performance-loss-with-glmapbuffer

- * - * @author James Seibel - * @version 12-1-2021 - */ -public class GLProxy -{ - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - - private static final ExecutorService workerThread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(GLProxy.class.getSimpleName() + "-Worker-Thread").build()); - - - private static GLProxy instance = null; - - /** Minecraft's GLFW window */ - public final long minecraftGlContext; - /** Minecraft's GL capabilities */ - public final GLCapabilities minecraftGlCapabilities; - - /** the LodBuilder's GLFW window */ - public final long lodBuilderGlContext; - /** the LodBuilder's GL capabilities */ - public final GLCapabilities lodBuilderGlCapabilities; - - /** the proxyWorker's GLFW window */ - public final long proxyWorkerGlContext; - /** the proxyWorker's GL capabilities */ - public final GLCapabilities proxyWorkerGlCapabilities; - - - - /** This program contains all shaders required when rendering LODs */ - public LodShaderProgram lodShaderProgram; - /** This is the VAO that is used when rendering */ - public final int vertexArrayObjectId; - - - /** Requires OpenGL 4.5, and offers the best buffer uploading */ - public final boolean bufferStorageSupported; - - /** Requires OpenGL 3.0 */ - public final boolean mapBufferRangeSupported; - - - - // May throw IllegalStateException, RuntimeException, FileNotFoundException - private GLProxy() - { - ClientApi.LOGGER.info("Lod: Creating " + GLProxy.class.getSimpleName() + "..."); - - // getting Minecraft's context has to be done on the render thread, - // where the GL context is - if (GLFW.glfwGetCurrentContext() == 0L) - 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(); - - - // context creation setup - GLFW.glfwDefaultWindowHints(); - // make the context window invisible - GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); - // by default the context should get the highest available OpenGL version - // but this can be explicitly set for testing -// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4); -// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 5); - - - // create the LodBuilder context - lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, minecraftGlContext); - GLFW.glfwMakeContextCurrent(lodBuilderGlContext); - lodBuilderGlCapabilities = GL.createCapabilities(); - - - // create the proxyWorker's context - proxyWorkerGlContext = GLFW.glfwCreateWindow(64, 48, "LOD proxy worker Window", 0L, minecraftGlContext); - GLFW.glfwMakeContextCurrent(proxyWorkerGlContext); - proxyWorkerGlCapabilities = GL.createCapabilities(); - - - - - - - //==================================// - // get any GPU related capabilities // - //==================================// - - setGlContext(GLProxyContext.LOD_BUILDER); - - ClientApi.LOGGER.info("Lod Render OpenGL version [" + GL11.glGetString(GL11.GL_VERSION) + "]."); - - // crash the game if the GPU doesn't support OpenGL 2.0 - if (!minecraftGlCapabilities.OpenGL20) - { - // Note: as of MC 1.17 this shouldn't happen since MC - // requires OpenGL 3.3, but just in case. - String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GLProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 2.0 or greater."; - MC.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 2.0 or greater.")); - } - - - - // get specific capabilities - bufferStorageSupported = lodBuilderGlCapabilities.glBufferStorage != 0; - mapBufferRangeSupported = lodBuilderGlCapabilities.glMapBufferRange != 0; - - // display the capabilities - if (!bufferStorageSupported) - { - String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5"; - ClientApi.LOGGER.error("This GPU doesn't support Buffer Storage (OpenGL 4.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance."); - } - - - // if using AUTO gpuUpload - // determine a good default for the GPU - if (CONFIG.client().advanced().buffers().getGpuUploadMethod() == GpuUploadMethod.AUTO) - { - GpuUploadMethod uploadMethod; - String vendor = GL15.glGetString(GL15.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION" - if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE")) - { - // NVIDIA card - - if (bufferStorageSupported) - { - uploadMethod = GpuUploadMethod.BUFFER_STORAGE; - } - else - { - uploadMethod = GpuUploadMethod.SUB_DATA; - } - } - else - { - // AMD or Intel card - - if (mapBufferRangeSupported) - { - uploadMethod = GpuUploadMethod.BUFFER_MAPPING; - } - else - { - uploadMethod = GpuUploadMethod.DATA; - } - } - - CONFIG.client().advanced().buffers().setGpuUploadMethod(uploadMethod); - ClientApi.LOGGER.info("GPU Vendor [" + vendor + "], Upload method set to [" + uploadMethod + "]."); - } - - - - - //==============// - // shader setup // - //==============// - - setGlContext(GLProxyContext.MINECRAFT); - - createShaderProgram(); - - // Note: VAO objects can not be shared between contexts, - // this must be created on minecraft's render context to work correctly - vertexArrayObjectId = GL30.glGenVertexArrays(); - - - - - - //==========// - // clean up // - //==========// - - // Since this is created on the render thread, make sure the Minecraft context is used in the end - setGlContext(GLProxyContext.MINECRAFT); - - - // GLProxy creation success - ClientApi.LOGGER.info(GLProxy.class.getSimpleName() + " creation completed"); - } - - /** Creates all required shaders */ - // May throw RuntimeException, FileNotFoundException - public void createShaderProgram() - { - LodShader vertexShader = null; - LodShader fragmentShader = null; - - // get the shaders from the resource folder - // Use File.separator ONLY if the file is external (not in the resourse), otherwise use "/" - vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/standard.vert", false); - fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/flat_shaded.frag", false); - - // this can be used when testing shaders, - // since we can't hot swap the files in the resource folder - // vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "C:/Users/James Seibel/Desktop/shaders/standard.vert", true); - // fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/flat_shaded.frag", true); - - - // create the shaders - lodShaderProgram = new LodShaderProgram(); - - // Attach the compiled shaders to the program, throws RuntimeException on link error - lodShaderProgram.attachShader(vertexShader); - lodShaderProgram.attachShader(fragmentShader); - - // activate the fragment shader output - GL30.glBindFragDataLocation(lodShaderProgram.id, 0, "fragColor"); - - // attach the shader program to the OpenGL context - lodShaderProgram.link(); - - // after the shaders have been attached to the program - // we don't need their OpenGL references anymore - GL20.glDeleteShader(vertexShader.id); - GL20.glDeleteShader(fragmentShader.id); - - } - - - /** - * 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 are already there. - if (currentContext == newContext) - return; - - - long contextPointer; - 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; - - case PROXY_WORKER: - contextPointer = proxyWorkerGlContext; - newGlCapabilities = proxyWorkerGlCapabilities; - break; - - default: // default should never happen, it is just here to make the compiler happy - case NONE: - // 0L is equivalent to null - contextPointer = 0L; - break; - } - - GLFW.glfwMakeContextCurrent(contextPointer); - GL.setCapabilities(newGlCapabilities); - } - - /** 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 == proxyWorkerGlContext) - return GLProxyContext.PROXY_WORKER; - else if (currentContext == 0L) - return GLProxyContext.NONE; - else - // hopefully this shouldn't happen - throw new IllegalStateException(Thread.currentThread().getName() + - " has a unknown OpenGl context: [" + currentContext + "]. " - + "Minecraft context [" + minecraftGlContext + "], " - + "LodBuilder context [" + lodBuilderGlContext + "], " - + "ProxyWorker context [" + proxyWorkerGlContext + "], " - + "no context [0]."); - } - - - public static GLProxy getInstance() - { - if (instance == null) - instance = new GLProxy(); - - return instance; - } - - - - - - - - - /** - * Asynchronously calls the given runnable on proxy's OpenGL context. - * Useful for creating/destroying OpenGL objects in a thread - * that doesn't normally have access to a OpenGL context.
- * No rendering can be done through this method. - */ - public void recordOpenGlCall(Runnable renderCall) - { - workerThread.execute(new Thread(() -> { runnableContainer(renderCall); })); - } - private void runnableContainer(Runnable renderCall) - { - try - { - // set up the context... - setGlContext(GLProxyContext.PROXY_WORKER); - // ...run the actual code... - renderCall.run(); - } - catch (Exception e) - { - ClientApi.LOGGER.error(Thread.currentThread().getName() + " ran into a issue: " + e.getMessage()); - e.printStackTrace(); - } - finally - { - // ...and make sure the context is released when the thread finishes - setGlContext(GLProxyContext.NONE); - } - } - - /** - * If called from a legacy OpenGL context this will - * set the fog end to infinity with a density of 0. - * Effectively removing the fog. - *

- * This only works with Legacy OpenGL because James hasn't - * looking into a way for it to work with Modern OpenGL. - */ - public void disableLegacyFog() - { - // make sure this is a legacy OpenGL context - if (minecraftGlCapabilities.glFogf != 0) - { - // glFogf should only have an address if the current OpenGL - // context can call it, and it should only be able to call it in - // legacy OpenGL contexts; since it is disabled in Modern - // OpenGL. - - GL11.glFogf(GL11.GL_FOG_START, 0.0f); - GL11.glFogf(GL11.GL_FOG_END, Float.MAX_VALUE); - GL11.glFogf(GL11.GL_FOG_DENSITY, 0.0f); - } - } - - -} diff --git a/src/main/java/com/seibel/lod/core/render/LodRenderer.java b/src/main/java/com/seibel/lod/core/render/LodRenderer.java deleted file mode 100644 index 2bb298b48..000000000 --- a/src/main/java/com/seibel/lod/core/render/LodRenderer.java +++ /dev/null @@ -1,777 +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.core.render; - -import java.awt.Color; -import java.util.HashSet; - -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; -import org.lwjgl.opengl.GL30; - -import com.seibel.lod.core.api.ApiShared; -import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory; -import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.VertexBuffersAndOffset; -import com.seibel.lod.core.enums.config.GpuUploadMethod; -import com.seibel.lod.core.enums.rendering.DebugMode; -import com.seibel.lod.core.enums.rendering.FogColorMode; -import com.seibel.lod.core.enums.rendering.FogDistance; -import com.seibel.lod.core.enums.rendering.FogDrawMode; -import com.seibel.lod.core.handlers.IReflectionHandler; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.objects.lod.RegionPos; -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.objects.opengl.LodVertexBuffer; -import com.seibel.lod.core.objects.rending.LodFogConfig; -import com.seibel.lod.core.render.shader.LodShaderProgram; -import com.seibel.lod.core.util.DetailDistanceUtil; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -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.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper; - -/** - * This is where all the magic happens.
- * This is where LODs are draw to the world. - * - * @author James Seibel - * @version 11-27-2021 - */ -public class LodRenderer -{ - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IReflectionHandler REFLECTION_HANDLER = SingletonHandler.get(IReflectionHandler.class); - private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class); - - - /** - * If true the LODs colors will be replaced with - * a checkerboard, this can be used for debugging. - */ - public DebugMode previousDebugMode = DebugMode.OFF; - - private int farPlaneBlockDistance; - - - /** This is used to generate the buildable buffers */ - private final LodBufferBuilderFactory lodBufferBuilderFactory; - - /** Each VertexBuffer represents 1 region */ - private LodVertexBuffer[][][] 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) - */ - @SuppressWarnings("unused") - private int[][][] storageBufferIds; - - private AbstractChunkPosWrapper vbosCenter = FACTORY.createChunkPos(); - - - /** 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; - - - - - public LodRenderer(LodBufferBuilderFactory newLodNodeBufferBuilder) - { - lodBufferBuilderFactory = 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 mcModelViewMatrix This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method - * @param mcProjectionMatrix - * @param partialTicks how far into the current tick this method was called. - */ - public void drawLODs(LodDimension lodDim, Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) - { - //=================================// - // determine if LODs should render // - //=================================// - - if (lodDim == null) - { - // if there aren't any loaded LodChunks - // don't try drawing anything - return; - } - - if (MC_RENDER.playerHasBlindnessEffect()) - { - // if the player is blind, don't render LODs, - // and don't change minecraft's fog - // which blindness relies on. - return; - } - - if (CONFIG.client().graphics().fogQuality().getDisableVanillaFog()) - GLProxy.getInstance().disableLegacyFog(); - - - - - // TODO move the buffer regeneration logic into its own class (probably called in the client api 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 ((partialRegen || fullRegen) && !lodBufferBuilderFactory.generatingBuffers && !lodBufferBuilderFactory.newBuffersAvailable()) - { - // generate the LODs on a separate thread to prevent stuttering or freezing - lodBufferBuilderFactory.generateLodBuffersAsync(this, lodDim, MC.getPlayerBlockPos(), true); - - // the regen process has been started, - // it will be done when lodBufferBuilder.newBuffersAvailable() - // is true - fullRegen = false; - partialRegen = false; - } - - // TODO move the buffer regeneration logic into its own class (probably called in the client api instead) - // ...ending here - - if (lodBufferBuilderFactory.newBuffersAvailable()) - { - swapBuffers(); - } - - - - - - //===============// - // initial setup // - //===============// - - profiler.push("LOD setup"); - - GLProxy glProxy = GLProxy.getInstance(); - - - - // set the required open GL settings - - if (CONFIG.client().advanced().debugging().getDebugMode() == 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.glEnable(GL15.GL_CULL_FACE); - 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); - - // get MC's shader program - int currentProgram = GL20.glGetInteger(GL20.GL_CURRENT_PROGRAM); - - - Mat4f modelViewMatrix = translateModelViewMatrix(mcModelViewMatrix, partialTicks); - vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; - // required for setupFog and setupProjectionMatrix - if (MC.getWrappedClientWorld().getDimensionType().hasCeiling()) - farPlaneBlockDistance = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH; - else - farPlaneBlockDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * LodUtil.CHUNK_WIDTH; - - - Mat4f projectionMatrix = createProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance); - - LodFogConfig fogSettings = determineFogConfig(); - - - - - - - if (vbos != null) - { - //==============// - // shader setup // - //==============// - - // can be used when testing shaders - // glProxy.createShaderProgram(); - - - LodShaderProgram shaderProgram = glProxy.lodShaderProgram; - shaderProgram.use(); - - - // determine the VertexArrayObject's element positions - int posAttrib = shaderProgram.getAttributeLocation("vPosition"); - shaderProgram.enableVertexAttribute(posAttrib); - int colAttrib = shaderProgram.getAttributeLocation("color"); - shaderProgram.enableVertexAttribute(colAttrib); - - - // global uniforms - int mvmUniform = shaderProgram.getUniformLocation("modelViewMatrix"); - shaderProgram.setUniform(mvmUniform, modelViewMatrix); - int projUniform = shaderProgram.getUniformLocation("projectionMatrix"); - shaderProgram.setUniform(projUniform, projectionMatrix); - int cameraUniform = shaderProgram.getUniformLocation("cameraPos"); - shaderProgram.setUniform(cameraUniform, getTranslatedCameraPos()); - int fogColorUniform = shaderProgram.getUniformLocation("fogColor"); - shaderProgram.setUniform(fogColorUniform, getFogColor()); - - - // region dependent uniforms - int fogEnabledUniform = shaderProgram.getUniformLocation("fogEnabled"); - int nearFogEnabledUniform = shaderProgram.getUniformLocation("nearFogEnabled"); - int farFogEnabledUniform = shaderProgram.getUniformLocation("farFogEnabled"); - // near - int nearFogStartUniform = shaderProgram.getUniformLocation("nearFogStart"); - int nearFogEndUniform = shaderProgram.getUniformLocation("nearFogEnd"); - // far - int farFogStartUniform = shaderProgram.getUniformLocation("farFogStart"); - int farFogEndUniform = shaderProgram.getUniformLocation("farFogEnd"); - - - - - - //===========// - // rendering // - //===========// - - profiler.popPush("LOD draw"); - - boolean cullingDisabled = CONFIG.client().graphics().advancedGraphics().getDisableDirectionalCulling(); - boolean renderBufferStorage = CONFIG.client().advanced().buffers().getGpuUploadMethod() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported; - - // where the center of the buffers is (needed when culling regions) - RegionPos vboCenterRegionPos = new RegionPos(vbosCenter); - RegionPos vboPos = new RegionPos(); - - - // render each of the buffers - for (int x = 0; x < vbos.length; x++) - { - for (int z = 0; z < vbos.length; z++) - { - vboPos.x = x + vboCenterRegionPos.x - (lodDim.getWidth() / 2); - vboPos.z = z + vboCenterRegionPos.z - (lodDim.getWidth() / 2); - - if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(), MC_RENDER.getLookAtVector(), vboPos.blockPos())) - { - // fog may be different from region to region - applyFog(shaderProgram, - fogSettings, fogEnabledUniform, nearFogEnabledUniform, farFogEnabledUniform, - nearFogStartUniform, nearFogEndUniform, farFogStartUniform, farFogEndUniform); - - - // actual rendering - int bufferId = 0; - for (int i = 0; i < vbos[x][z].length; i++) - { - bufferId = (storageBufferIds != null && renderBufferStorage) ? storageBufferIds[x][z][i] : vbos[x][z][i].id; - drawArrays(bufferId, vbos[x][z][i].vertexCount, posAttrib, colAttrib); - } - - } - } - } - - - - //================// - // render cleanup // - //================// - - // if this cleanup isn't done MC may crash - // when trying to render its own terrain - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - GL30.glBindVertexArray(0); - - GL20.glDisableVertexAttribArray(posAttrib); - GL20.glDisableVertexAttribArray(colAttrib); - } - - - - - - //=========// - // cleanup // - //=========// - - profiler.popPush("LOD cleanup"); - - GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL); - GL15.glDisable(GL15.GL_BLEND); // TODO: what should this be reset to? - - GL20.glUseProgram(currentProgram); - - // clear the depth buffer so everything 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 drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib) - { - if (glBufferId == 0) - return; - - // can be used to check for OpenGL errors -// int error = GL15.glGetError(); -// ClientProxy.LOGGER.info(Integer.toHexString(error)); - - - // bind the buffer we are going to draw - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, glBufferId); - GL30.glBindVertexArray(GLProxy.getInstance().vertexArrayObjectId); - - // let OpenGL know how our buffer is set up - int vertexByteCount = (Float.BYTES * 3) + (Byte.BYTES * 4); - GL20.glEnableVertexAttribArray(posAttrib); - GL20.glVertexAttribPointer(posAttrib, 3, GL15.GL_FLOAT, false, vertexByteCount, 0); - GL20.glEnableVertexAttribArray(colAttrib); - GL20.glVertexAttribPointer(colAttrib, 4, GL15.GL_UNSIGNED_BYTE, true, vertexByteCount, Float.BYTES * 3); - - - // draw the LODs - GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, vertexCount); - } - - - - - - - //=================// - // Setup Functions // - //=================// - - - /** Create all buffers that will be used. */ - public void setupBuffers(LodDimension lodDim) - { - lodBufferBuilderFactory.setupBuffers(lodDim); - } - - - - - /** Return what fog settings should be used when rendering. */ - private LodFogConfig determineFogConfig() - { - LodFogConfig fogConfig = new LodFogConfig(); - - - fogConfig.fogDrawMode = CONFIG.client().graphics().fogQuality().getFogDrawMode(); - if (fogConfig.fogDrawMode == FogDrawMode.USE_OPTIFINE_SETTING) - fogConfig.fogDrawMode = REFLECTION_HANDLER.getFogDrawMode(); - - - // how different distances are drawn depends on the quality set - fogConfig.fogDistance = CONFIG.client().graphics().fogQuality().getFogDistance(); - - - - - - // far fog // - - if (CONFIG.client().graphics().fogQuality().getFogDistance() == FogDistance.NEAR_AND_FAR) - fogConfig.farFogStart = farPlaneBlockDistance * 1.6f * 0.9f; - else - // for more realistic fog when using FAR - fogConfig.farFogStart = Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f); - - fogConfig.farFogEnd = farPlaneBlockDistance * 1.6f; - - - // near fog // - - // the reason that I wrote fogEnd then fogStart backwards - // is because we are using fog backwards to how - // it is normally used, hiding near objects - // instead of far objects. - fogConfig.nearFogEnd = vanillaBlockRenderedDistance * 1.41f; - fogConfig.nearFogStart = vanillaBlockRenderedDistance * 1.6f; - - - return fogConfig; - } - - private Color getFogColor() - { - Color fogColor; - - if (CONFIG.client().graphics().fogQuality().getFogColorMode() == FogColorMode.USE_SKY_COLOR) - fogColor = MC_RENDER.getSkyColor(); - else - fogColor = MC_RENDER.getFogColor(); - - return fogColor; - } - - /** - * 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 Mat4f translateModelViewMatrix(Mat4f mcModelViewMatrix, float partialTicks) - { - // get all relevant camera info - Vec3d projectedView = MC_RENDER.getCameraExactPosition(); - - // 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) - AbstractBlockPosWrapper bufferPos = vbosCenter.getWorldPosition(); - double xDiff = projectedView.x - bufferPos.getX(); - double zDiff = projectedView.z - bufferPos.getZ(); - mcModelViewMatrix.multiplyTranslationMatrix(-xDiff, -projectedView.y, -zDiff); - - return mcModelViewMatrix; - } - - /** - * Similar to translateModelViewMatrix (above), - * but for the camera position - */ - private Vec3f getTranslatedCameraPos() - { - AbstractBlockPosWrapper worldCenter = vbosCenter.getWorldPosition(); - Vec3d cameraPos = MC_RENDER.getCameraExactPosition(); - return new Vec3f((float)cameraPos.x - worldCenter.getX(), (float)cameraPos.y, (float)cameraPos.z - worldCenter.getZ()); - } - - /** - * create and return a new projection matrix based on MC's projection matrix - * @param currentProjectionMatrix this is Minecraft's current projection matrix - * @param vanillaBlockRenderedDistance Minecraft's vanilla far plane distance - */ - private Mat4f createProjectionMatrix(Mat4f currentProjectionMatrix, float vanillaBlockRenderedDistance) - { - //Create a copy of the current matrix, so the current matrix isn't modified. - Mat4f lodProj = currentProjectionMatrix.copy(); - - //Set new far and near clip plane values. - lodProj.setClipPlanes( - CONFIG.client().graphics().advancedGraphics().getUseExtendedNearClipPlane() ? vanillaBlockRenderedDistance / 5 : 1, - farPlaneBlockDistance * LodUtil.CHUNK_WIDTH / 2); - - return lodProj; - } - - private void applyFog(LodShaderProgram shaderProgram, - LodFogConfig fogSettings, int fogEnabledUniform, int nearFogEnabledUniform, int farFogEnabledUniform, - int nearFogStartUniform, int nearFogEndUniform, int farFogStartUniform, int farFogEndUniform) - { - if (fogSettings.fogDrawMode != FogDrawMode.FOG_DISABLED) - { - shaderProgram.setUniform(fogEnabledUniform, true); - shaderProgram.setUniform(nearFogEnabledUniform, fogSettings.fogDistance != FogDistance.FAR); - shaderProgram.setUniform(farFogEnabledUniform, fogSettings.fogDistance != FogDistance.NEAR); - - // near - shaderProgram.setUniform(nearFogStartUniform, fogSettings.nearFogStart); - shaderProgram.setUniform(nearFogEndUniform, fogSettings.nearFogEnd); - // far - shaderProgram.setUniform(farFogStartUniform, fogSettings.farFogStart); - shaderProgram.setUniform(farFogEndUniform, fogSettings.farFogEnd); - } - else - { - shaderProgram.setUniform(fogEnabledUniform, false); - } - } - - - - - - //======================// - // 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 = lodBufferBuilderFactory.getVertexBuffers(); - vbos = result.vbos; - storageBufferIds = result.storageBufferIds; - vbosCenter = result.drawableCenterChunkPos; - } - - /** Calls the BufferBuilder's destroyBuffers method. */ - public void destroyBuffers() - { - lodBufferBuilderFactory.destroyBuffers(); - } - - - - /** Determines if the LODs should have a fullRegen or partialRegen */ - private void determineIfLodsShouldRegenerate(LodDimension lodDim, float partialTicks) - { - short chunkRenderDistance = (short) MC_RENDER.getRenderDistance(); - int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2; - - //=============// - // full regens // - //=============// - - // check if the view distance changed - if (ApiShared.previousLodRenderDistance != CONFIG.client().graphics().quality().getLodChunkRenderDistance() - || chunkRenderDistance != prevRenderDistance - || prevFogDistance != CONFIG.client().graphics().fogQuality().getFogDistance()) - { - - vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; - DetailDistanceUtil.updateSettings(); - fullRegen = true; - previousPos = LevelPosUtil.createLevelPos((byte) 4, MC.getPlayerChunkPos().getZ(), MC.getPlayerChunkPos().getZ()); - prevFogDistance = CONFIG.client().graphics().fogQuality().getFogDistance(); - prevRenderDistance = chunkRenderDistance; - } - - // did the user change the debug setting? - if (CONFIG.client().advanced().debugging().getDebugMode() != previousDebugMode) - { - previousDebugMode = CONFIG.client().advanced().debugging().getDebugMode(); - fullRegen = true; - } - - - long newTime = System.currentTimeMillis(); - - // check if the player has moved - if (newTime - prevPlayerPosTime > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveTimeout) - { - if (LevelPosUtil.getDetailLevel(previousPos) == 0 - || Math.abs(MC.getPlayerChunkPos().getX() - LevelPosUtil.getPosX(previousPos)) > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveDistance - || Math.abs(MC.getPlayerChunkPos().getZ() - LevelPosUtil.getPosZ(previousPos)) > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveDistance) - { - 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 (CONFIG.client().advanced().buffers().getRebuildTimes()) - { - 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_RENDER.getGamma() != prevBrightness) - { - fullRegen = true; - prevBrightness = MC_RENDER.getGamma(); - prevSkyBrightness = skyBrightness; - } - - /*if (lightMap != lastLightMap) - { - fullRegen = true; - lastLightMap = lightMap; - }*/ - - //================// - // partial regens // - //================// - - - // check if the vanilla rendered chunks changed - if (newTime - prevVanillaChunkTime > CONFIG.client().advanced().buffers().getRebuildTimes().renderedChunkTimeout) - { - if (vanillaRenderedChunksChanged) - { - partialRegen = true; - vanillaRenderedChunksChanged = false; - } - prevVanillaChunkTime = newTime; - } - - - // check if there is any newly generated terrain to show - if (newTime - prevChunkTime > CONFIG.client().advanced().buffers().getRebuildTimes().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 (AbstractChunkPosWrapper pos : chunkPosToSkip) - { - vanillaRenderedChunksEmptySkip = false; - - xIndex = (pos.getX() - MC.getPlayerChunkPos().getX()) + (chunkRenderDistance + 1); - zIndex = (pos.getZ() - 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.getPlayerBlockPos().getY() > 256 && !vanillaRenderedChunksEmptySkip) - { - vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; - vanillaRenderedChunksChanged = true; - vanillaRenderedChunksEmptySkip = true; - } - - vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; - } - -} diff --git a/src/main/java/com/seibel/lod/core/render/RenderUtil.java b/src/main/java/com/seibel/lod/core/render/RenderUtil.java deleted file mode 100644 index 15130a691..000000000 --- a/src/main/java/com/seibel/lod/core/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.core.render; - -import com.seibel.lod.core.objects.math.Vec3f; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; - -/** - * 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 IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class); - - - /** - * 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(AbstractChunkPosWrapper pos, AbstractChunkPosWrapper center) - { - return (pos.getX() >= center.getX() - MC_RENDER.getRenderDistance() - && pos.getX() <= center.getX() + MC_RENDER.getRenderDistance()) - && - (pos.getZ() >= center.getZ() - MC_RENDER.getRenderDistance() - && pos.getZ() <= center.getZ() + MC_RENDER.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_RENDER.getRenderDistance() - && x <= centerCoordinate + MC_RENDER.getRenderDistance()) - && - (z >= centerCoordinate - MC_RENDER.getRenderDistance() - && z <= centerCoordinate + MC_RENDER.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(AbstractBlockPosWrapper playerBlockPos, Vec3f cameraDir, AbstractBlockPosWrapper vboCenterPos) - { - // convert the vbo position into a direction vector - // starting from the player's position - Vec3f vboVec = new Vec3f(vboCenterPos.getX(), 0, vboCenterPos.getZ()); - Vec3f playerVec = new Vec3f(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ()); - - vboVec.subtract(playerVec); - - - int halfRegionWidth = LodUtil.REGION_WIDTH / 2; - - // calculate the 4 corners - Vec3f vboSeVec = new Vec3f(vboVec.x + halfRegionWidth, vboVec.y, vboVec.z + halfRegionWidth); - Vec3f vboSwVec = new Vec3f(vboVec.x - halfRegionWidth, vboVec.y, vboVec.z + halfRegionWidth); - Vec3f vboNwVec = new Vec3f(vboVec.x - halfRegionWidth, vboVec.y, vboVec.z - halfRegionWidth); - Vec3f vboNeVec = new Vec3f(vboVec.x + halfRegionWidth, vboVec.y, vboVec.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(Vec3f objectVector, Vec3f 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.dotProduct(cameraDir) > -0.1; - } -} diff --git a/src/main/java/com/seibel/lod/core/render/shader/LodShader.java b/src/main/java/com/seibel/lod/core/render/shader/LodShader.java deleted file mode 100644 index 91a1ca397..000000000 --- a/src/main/java/com/seibel/lod/core/render/shader/LodShader.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.core.render.shader; - -import java.io.BufferedReader; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -import org.lwjgl.opengl.GL20; - -/** - * This object holds a OpenGL reference to a shader - * and allows for reading in and compiling a shader file. - * - * @author James Seibel - * @version 11-8-2021 - */ -public class LodShader -{ - /** OpenGL shader ID */ - public final int id; - - - - /** Creates a shader with specified type. */ - public LodShader(int type) - { - id = GL20.glCreateShader(type); - } - - - - /** - * Loads a shader from file. - * - * @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. - * @param path File path of the shader - * @param absoluteFilePath If false the file path is relative to the resource jar folder. - * @throws Exception if the shader fails to compile - */ - public static LodShader loadShader(int type, String path, boolean absoluteFilePath) - { - StringBuilder stringBuilder = new StringBuilder(); - - try - { - // open the file - InputStream in; - if (absoluteFilePath) { - // Throws FileNotFoundException - in = new FileInputStream(path); // Note: this should use OS path seperator - } else { - in = LodShader.class.getClassLoader().getResourceAsStream(path); // Note: path seperator should be '/' - if (in == null) { - throw new FileNotFoundException("Shader file not found in resource: "+path); - } - } - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - - // read in the file - String line; - while ((line = reader.readLine()) != null) - stringBuilder.append(line).append("\n"); - } - catch (IOException e) - { - throw new RuntimeException("Unable to load shader from file [" + path + "]. Error: " + e.getMessage()); - } - CharSequence shaderFileSource = stringBuilder.toString(); - - return createShader(type, shaderFileSource); - } - - /** - * Creates a shader with the specified type and source. - * - * @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. - * @param source Source of the shader - * @throws Exception if the shader fails to compile - */ - public static LodShader createShader(int type, CharSequence source) - { - LodShader shader = new LodShader(type); - GL20.glShaderSource(shader.id, source); - shader.compile(); - - return shader; - } - - /** - * Compiles the shader and checks its status afterwards. - * @throws Exception if the shader fails to compile - */ - public void compile() - { - GL20.glCompileShader(id); - - // check if the shader compiled - int status = GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS); - if (status != GL20.GL_TRUE) - throw new RuntimeException("Shader compiler error. Details: "+GL20.glGetShaderInfoLog(id)); - } - -} diff --git a/src/main/java/com/seibel/lod/core/render/shader/LodShaderProgram.java b/src/main/java/com/seibel/lod/core/render/shader/LodShaderProgram.java deleted file mode 100644 index e8c59b8dd..000000000 --- a/src/main/java/com/seibel/lod/core/render/shader/LodShaderProgram.java +++ /dev/null @@ -1,203 +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.core.render.shader; - -import java.awt.Color; -import java.nio.FloatBuffer; - -import org.lwjgl.opengl.GL20; -import org.lwjgl.system.MemoryStack; - -import com.seibel.lod.core.objects.math.Mat4f; -import com.seibel.lod.core.objects.math.Vec3d; -import com.seibel.lod.core.objects.math.Vec3f; - - -/** - * This object holds the reference to a OpenGL shader program - * and contains a few methods that can be used with OpenGL shader programs. - * The reason for many of these simple wrapper methods is as reminders of what - * can (and needs to be) done with a shader program. - * - * @author James Seibel - * @version 11-26-2021 - */ -public class LodShaderProgram -{ - /** Stores the handle of the program. */ - public final int id; - - /** Creates a shader program. */ - public LodShaderProgram() - { - id = GL20.glCreateProgram(); - } - - - - /** Calls GL20.glUseProgram(this.id) */ - public void use() - { - GL20.glUseProgram(id); - } - - /** - * Calls GL20.glAttachShader(this.id, shader.id) - * - * @param shader Shader to get attached - */ - public void attachShader(LodShader shader) - { - GL20.glAttachShader(this.id, shader.id); - } - - - /** - * Links the shader program to the current OpenGL context. - * @throws Exception Exception if the program failed to link - */ - public void link() - { - GL20.glLinkProgram(this.id); - checkLinkStatus(); - } - - /** - * Checks if the program was linked successfully. - * @throws Exception if the program failed to link - */ - public void checkLinkStatus() - { - int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS); - if (status != GL20.GL_TRUE) - throw new RuntimeException("Shader Link Error. Details: "+GL20.glGetProgramInfoLog(this.id)); - } - - - - - /** - * Gets the location of an attribute variable with specified name. - * Calls GL20.glGetAttribLocation(id, name) - * - * @param name Attribute name - * - * @return Location of the attribute - */ - public int getAttributeLocation(CharSequence name) - { - return GL20.glGetAttribLocation(id, name); - } - - /** - * Calls GL20.glEnableVertexAttribArray(location) - * - * @param location Location of the vertex attribute - */ - public void enableVertexAttribute(int location) - { - GL20.glEnableVertexAttribArray(location); - } - - /** - * Calls GL20.glDisableVertexAttribArray(location) - * - * @param location Location of the vertex attribute - */ - public void disableVertexAttribute(int location) - { - GL20.glDisableVertexAttribArray(location); - } - - /** - * Sets the vertex attribute pointer. - * Calls GL20.glVertexAttribPointer(...) - * - * @param location Location of the vertex attribute - * @param size Number of values per vertex - * @param stride Offset between consecutive generic vertex attributes in - * bytes - * @param offset Offset of the first component of the first generic vertex - * attribute in bytes - */ - public void pointVertexAttribute(int location, int size, int stride, int offset) - { - GL20.glVertexAttribPointer(location, size, GL20.GL_FLOAT, false, stride, offset); - } - - /** - * Gets the location of a uniform variable with specified name. - * Calls GL20.glGetUniformLocation(id, name) - * - * @param name Uniform name - * - * @return -1 = error value, 0 = first value, 1 = second value, etc. - */ - public int getUniformLocation(CharSequence name) - { - return GL20.glGetUniformLocation(id, name); - } - - - - public void setUniform(int location, boolean value) - { - GL20.glUniform1i(location, value ? 1 : 0); - } - - public void setUniform(int location, int value) - { - GL20.glUniform1i(location, value); - } - - public void setUniform(int location, float value) - { - GL20.glUniform1f(location, value); - } - - public void setUniform(int location, Vec3f value) - { - GL20.glUniform3f(location, value.x, value.y, value.z); - } - - public void setUniform(int location, Vec3d value) - { - GL20.glUniform3f(location, (float) value.x, (float) value.y, (float) value.z); - } - - public void setUniform(int location, Mat4f value) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - FloatBuffer buffer = stack.mallocFloat(4 * 4); - value.store(buffer); - GL20.glUniformMatrix4fv(location, false, buffer); - } - } - - /** Converts the color's RGBA values into values between 0 and 1. */ - public void setUniform(int location, Color value) - { - GL20.glUniform4f(location, value.getRed() / 256.0f, value.getGreen() / 256.0f, value.getBlue() / 256.0f, value.getAlpha() / 256.0f); - } - - - -} diff --git a/src/main/java/com/seibel/lod/core/util/ColorUtil.java b/src/main/java/com/seibel/lod/core/util/ColorUtil.java deleted file mode 100644 index 1933246d2..000000000 --- a/src/main/java/com/seibel/lod/core/util/ColorUtil.java +++ /dev/null @@ -1,122 +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.core.util; - -import java.awt.Color; - -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; - -/** - * - * @author Cola - * @author Leonardo Amato - * @version 11-13-2021 - */ -public class ColorUtil -{ - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - - - 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; - } - - /** Returns a value between 0 and 255 */ - public static int getAlpha(int color) - { - return (color >> 24) & 0xFF; - } - - /** Returns a value between 0 and 255 */ - public static int getRed(int color) - { - return (color >> 16) & 0xFF; - } - - /** Returns a value between 0 and 255 */ - public static int getGreen(int color) - { - return (color >> 8) & 0xFF; - } - - /** Returns a value between 0 and 255 */ - 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 = MC.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/core/util/DataPointUtil.java b/src/main/java/com/seibel/lod/core/util/DataPointUtil.java deleted file mode 100644 index 81b11c287..000000000 --- a/src/main/java/com/seibel/lod/core/util/DataPointUtil.java +++ /dev/null @@ -1,521 +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.core.util; - -import static com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.skyLightPlayer; - -import com.seibel.lod.core.enums.config.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 final short VERTICAL_OFFSET = -64; - public static int WORLD_HEIGHT = 1024; - - 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((WORLD_HEIGHT / 2 + 1) * 2); - long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0)); - - - int genMode = DistanceGenerationMode.FULL.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 = WORLD_HEIGHT - VERTICAL_OFFSET; - 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.FULL.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/core/util/DetailDistanceUtil.java b/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java deleted file mode 100644 index 3976a46c7..000000000 --- a/src/main/java/com/seibel/lod/core/util/DetailDistanceUtil.java +++ /dev/null @@ -1,174 +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.core.util; - -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.enums.config.HorizontalQuality; -import com.seibel.lod.core.enums.config.HorizontalResolution; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; - -/** - * - * @author Leonardo Amato - * @version ?? - */ -public class DetailDistanceUtil -{ - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class); - - 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 = CONFIG.client().graphics().quality().getDrawResolution().detailLevel; - private static byte minDrawDetail = CONFIG.client().graphics().quality().getDrawResolution().detailLevel; - private static final int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1; - private static final int minDistance = 0; - private static int minDetailDistance = (int) (MC_RENDER.getRenderDistance()*16 * 1.42f); - private static int maxDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 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) (MC_RENDER.getRenderDistance()*16 * 1.42f); - minGenDetail = CONFIG.client().graphics().quality().getDrawResolution().detailLevel; - minDrawDetail = (byte) Math.max(CONFIG.client().graphics().quality().getDrawResolution().detailLevel, CONFIG.client().graphics().quality().getDrawResolution().detailLevel); - maxDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 16 * 8; - } - - public static int baseDistanceFunction(int detail) - { - if (detail <= minGenDetail) - return minDistance; - if (detail >= maxDetail) - return maxDistance; - - if (CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality()) - return detail * 0x10000; //if you want more you are doing wrong - - int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale() * 16; - if (CONFIG.client().graphics().quality().getHorizontalQuality() == HorizontalQuality.LOWEST) - return (detail * distanceUnit); - else - { - double base = CONFIG.client().graphics().quality().getHorizontalQuality().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) - { - byte detail; - if (distance == 0 - || (distance < minDetailDistance && useRenderMinDistance) - || CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality()) - return minDetail; - int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale() * 16; - if (CONFIG.client().graphics().quality().getHorizontalQuality() == HorizontalQuality.LOWEST) - detail = (byte) (distance / distanceUnit); - else - { - double base = CONFIG.client().graphics().quality().getHorizontalQuality().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 CONFIG.client().worldGenerator().getDistanceGenerationMode(); - } - - public static byte getLodDrawDetail(byte detail) - { - detail += minDrawDetail; - if (detail > 10) - detail = 10; - return 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 CONFIG.client().graphics().quality().getVerticalQuality().maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)]; - } - -} diff --git a/src/main/java/com/seibel/lod/core/util/LevelPosUtil.java b/src/main/java/com/seibel/lod/core/util/LevelPosUtil.java deleted file mode 100644 index 2dfe29440..000000000 --- a/src/main/java/com/seibel/lod/core/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.core.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/core/util/LodThreadFactory.java b/src/main/java/com/seibel/lod/core/util/LodThreadFactory.java deleted file mode 100644 index c12e8e0ff..000000000 --- a/src/main/java/com/seibel/lod/core/util/LodThreadFactory.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.core.util; - -import java.util.concurrent.ThreadFactory; - -/** - * Just a simple ThreadFactory to name ExecutorService - * threads, which can be helpful when debugging. - * @author James Seibel - * @version 8-15-2021 - */ -public class LodThreadFactory implements ThreadFactory -{ - public final String threadName; - - - public LodThreadFactory(String newThreadName) - { - threadName = newThreadName + " Thread"; - } - - @Override - public Thread newThread(Runnable r) - { - return new Thread(r, threadName); - } - -} diff --git a/src/main/java/com/seibel/lod/core/util/LodUtil.java b/src/main/java/com/seibel/lod/core/util/LodUtil.java deleted file mode 100644 index f87c85f0a..000000000 --- a/src/main/java/com/seibel/lod/core/util/LodUtil.java +++ /dev/null @@ -1,427 +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.core.util; - -import java.awt.Color; -import java.io.File; -import java.util.HashSet; - -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.enums.config.HorizontalResolution; -import com.seibel.lod.core.enums.config.VanillaOverdraw; -import com.seibel.lod.core.objects.VertexOptimizer; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.objects.lod.RegionPos; -import com.seibel.lod.core.objects.opengl.DefaultLodVertexFormats; -import com.seibel.lod.core.objects.opengl.LodVertexFormat; -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.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - -/** - * This class holds methods and constants that may be used in multiple places. - * - * @author James Seibel - * @version 11-13-2021 - */ -public class LodUtil -{ - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class); - - /** - * 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; - - - /** - * 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.

- *

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

- *

- * https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx - */ - public static final int MAX_ALLOCATABLE_DIRECT_MEMORY = 64 * 1024 * 1024; - - /** the format of data stored in the GPU buffers */ - public static final LodVertexFormat LOD_VERTEX_FORMAT = DefaultLodVertexFormats.POSITION_COLOR; - - - - - - /** - * Gets the ServerWorld for the relevant dimension. - * @return null if there is no ServerWorld for the given dimension - */ - public static IWorldWrapper getServerWorldFromDimension(IDimensionTypeWrapper newDimension) - { - if(!MC.hasSinglePlayerServer()) - return null; - - Iterable worlds = MC.getAllServerWorlds(); - IWorldWrapper returnWorld = null; - - for (IWorldWrapper world : worlds) - { - if (world.getDimensionType() == newDimension) - { - 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)); - } - - - /** - * 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(IWorldWrapper 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(IWorldWrapper world) - { - if (MC.hasSinglePlayerServer()) - { - // this will return the world save location - // and the dimension folder - - IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(world.getDimensionType()); - if (serverWorld == null) - throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the WorldWrapper for the dimension " + world.getDimensionType().getDimensionName()); - - return serverWorld.getSaveFolder().toString(); - } - else - { - return getServerId() + File.separatorChar + "dim_" + world.getDimensionType().getDimensionName() + File.separatorChar; - } - } - - /** returns the server name, IP and game version. */ - public static String getServerId() - { - String serverName = MC.getCurrentServerName().replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); - String serverIp = MC.getCurrentServerIp().replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); - String serverMcVersion = MC.getCurrentServerVersion().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, AbstractBlockPosWrapper blockPosWrapper) - { - int chunkRenderDist = MC_RENDER.getRenderDistance(); - AbstractChunkPosWrapper centerChunk = FACTORY.createChunkPos(blockPosWrapper); - - int skipRadius; - VanillaOverdraw overdraw = CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw(); - HorizontalResolution drawRes = CONFIG.client().graphics().quality().getDrawResolution(); - - // 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 = MC_RENDER.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(FACTORY.createChunkPos(x, z)); - } - } - } - return posToSkip; - } - - - /** - * 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 (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS) - { - tempX = x + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).x; - tempZ = z + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).z; - if (vanillaRenderedChunks[x][z] || (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length) - && !vanillaRenderedChunks[tempX][tempZ])) - return true; - } - return false; - } - - - /** This is copied from Minecraft's MathHelper class */ - public static float fastInvSqrt(float numb) - { - float half = 0.5F * numb; - int i = Float.floatToIntBits(numb); - i = 1597463007 - (i >> 1); - numb = Float.intBitsToFloat(i); - return numb * (1.5F - half * numb * numb); - } -} diff --git a/src/main/java/com/seibel/lod/core/util/SingletonHandler.java b/src/main/java/com/seibel/lod/core/util/SingletonHandler.java deleted file mode 100644 index 1770aef20..000000000 --- a/src/main/java/com/seibel/lod/core/util/SingletonHandler.java +++ /dev/null @@ -1,98 +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.core.util; - -import java.util.HashMap; -import java.util.Map; - -/** - * This class takes care of dependency injection - * for singletons. - * - * @author James Seibel - * @version 11-20-2021 - */ -public class SingletonHandler -{ - private static final Map, Object> singletons = new HashMap, Object>(); - - - - - - /** - * Adds the given singleton so it can be referenced later. - * - * @param interfaceClass - * @param singletonReference - * @throws IllegalStateException - */ - public static void bind(Class interfaceClass, Object singletonReference) throws IllegalStateException - { - // make sure we haven't already bound this singleton - if (singletons.containsKey(interfaceClass)) - { - throw new IllegalStateException("The singleton [" + interfaceClass.getSimpleName() + "] has already been bound."); - } - - - // make sure the given singleton implements the interface - boolean singletonImplementsInterface = false; - for (Class singletonInterface : singletonReference.getClass().getInterfaces()) - { - if (singletonInterface.equals(interfaceClass)) - { - singletonImplementsInterface = true; - break; - } - } - if (!singletonImplementsInterface) - { - throw new IllegalStateException("The singleton [" + interfaceClass.getSimpleName() + "] doesn't implement the interface [" + interfaceClass.getSimpleName() + "]."); - } - - - singletons.put(interfaceClass, singletonReference); - } - - /** - * Returns a singleton of type T - * if one has been bound. - * - * @param class of the singleton - * @param objectClass class of the singleton, but as a parameter! - * @return the singleton of type T - * @throws NullPointerException if no singleton of type T has been bound. - * @throws ClassCastException if the singleton isn't able to be cast to type T. (this shouldn't normally happen, unless the bound object changed somehow) - */ - @SuppressWarnings("unchecked") - public static T get(Class objectClass) throws NullPointerException, ClassCastException - { - // throw an error if the given singleton doesn't exist. - if (!singletons.containsKey(objectClass)) - { - throw new NullPointerException("The singleton [" + objectClass.getSimpleName() + "] was never bound. If you are calling [bind], make sure it is happening before you call [get]."); - } - - - return (T) singletons.get(objectClass); - } - -} diff --git a/src/main/java/com/seibel/lod/core/util/ThreadMapUtil.java b/src/main/java/com/seibel/lod/core/util/ThreadMapUtil.java deleted file mode 100644 index 7cf8405b3..000000000 --- a/src/main/java/com/seibel/lod/core/util/ThreadMapUtil.java +++ /dev/null @@ -1,218 +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.core.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.core.enums.LodDirection; -import com.seibel.lod.core.objects.VertexOptimizer; - -/** - * 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[VertexOptimizer.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(LodDirection.NORTH) == null) - || (adjDataMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH).length != verticalData)) - { - adjDataMap.put(Thread.currentThread().getName(), new HashMap<>()); - adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.UP, new long[1]); - adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.DOWN, new long[1]); - for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS) - adjDataMap.get(Thread.currentThread().getName()).put(lodDirection, new long[verticalData]); - } - else - { - - for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS) - Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(lodDirection), DataPointUtil.EMPTY_DATA); - } - return adjDataMap.get(Thread.currentThread().getName()); - } - - public static VertexOptimizer getBox() - { - if (!boxMap.containsKey(Thread.currentThread().getName()) - || (boxMap.get(Thread.currentThread().getName()) == null)) - { - boxMap.put(Thread.currentThread().getName(), new VertexOptimizer()); - } - 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.WORLD_HEIGHT / 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/core/wrapperInterfaces/IWrapperFactory.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java deleted file mode 100644 index 123aa33f7..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.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.core.wrapperInterfaces; - -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.objects.lod.LodDimension; -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; - -/** - * This handles creating abstract wrapper objects. - * - * @author James Seibel - * @version 11-18-2021 - */ -public interface IWrapperFactory -{ - AbstractBlockPosWrapper createBlockPos(); - AbstractBlockPosWrapper createBlockPos(int x, int y, int z); - - - AbstractChunkPosWrapper createChunkPos(); - AbstractChunkPosWrapper createChunkPos(int x, int z); - AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos); - AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos); - - - AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/AbstractBlockPosWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/AbstractBlockPosWrapper.java deleted file mode 100644 index a8cf65f6d..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/AbstractBlockPosWrapper.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.core.wrapperInterfaces.block; - -import com.seibel.lod.core.enums.LodDirection; - -/** - * BlockPos needs to be abstract instead of an interfaces - * so that we can define its constructors. - * - * @author James Seibel - * @version 11-20-2021 - */ -public abstract class AbstractBlockPosWrapper -{ - public AbstractBlockPosWrapper() { } - public AbstractBlockPosWrapper(int x, int y, int z) { } - - - - public abstract void set(int x, int y, int z); - - public abstract int getX(); - public abstract int getY(); - public abstract int getZ(); - - public abstract int get(LodDirection.Axis axis); - - /** returns itself */ - public abstract AbstractBlockPosWrapper offset(int x, int y, int z); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockColorSingletonWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockColorSingletonWrapper.java deleted file mode 100644 index aeef4ba9d..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockColorSingletonWrapper.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.core.wrapperInterfaces.block; - -/** - * 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 11-17-2021 - */ -public interface IBlockColorSingletonWrapper -{ - /** @returns the base color of water (grey) */ - IBlockColorWrapper getWaterColor(); -} - diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockColorWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockColorWrapper.java deleted file mode 100644 index 7bc4f82dc..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockColorWrapper.java +++ /dev/null @@ -1,50 +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.core.wrapperInterfaces.block; - -/** - * @author James Seibel - * @version 11-17-2021 - */ -public interface IBlockColorWrapper -{ - //--------------// - //Colors getters// - //--------------// - - boolean hasColor(); - - int getColor(); - - - //------------// - //Tint getters// - //------------// - - boolean hasTint(); - - boolean hasGrassTint(); - - boolean hasFolliageTint(); - - boolean hasWaterTint(); - -} - diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockShapeWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockShapeWrapper.java deleted file mode 100644 index 4a3b3448b..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/block/IBlockShapeWrapper.java +++ /dev/null @@ -1,39 +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.core.wrapperInterfaces.block; - -/** - * @author James Seibel - * @version 11-20-2021 - */ -public interface IBlockShapeWrapper -{ - boolean ofBlockToAvoid(); - - //-----------------// - //Avoidance getters// - //-----------------// - - boolean isNonFull(); - - boolean hasNoCollision(); - - boolean isToAvoid(); -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/chunk/AbstractChunkPosWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/chunk/AbstractChunkPosWrapper.java deleted file mode 100644 index 374d43f7b..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/chunk/AbstractChunkPosWrapper.java +++ /dev/null @@ -1,52 +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.core.wrapperInterfaces.chunk; - -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; - - -/** - * This is abstract instead of an interface, so - * we can define its constructors. - * - * @author James Seibel - * @version 11-18-2021 - */ -public abstract class AbstractChunkPosWrapper -{ - public AbstractChunkPosWrapper(AbstractChunkPosWrapper newChunkPos) { } - public AbstractChunkPosWrapper(AbstractBlockPosWrapper blockPos) { } - public AbstractChunkPosWrapper(int chunkX, int chunkZ) { } - public AbstractChunkPosWrapper() { } - - - - public abstract int getX(); - public abstract int getZ(); - - public abstract int getMinBlockX(); - public abstract int getMinBlockZ(); - - public abstract int getRegionX(); - public abstract int getRegionZ(); - - public abstract AbstractBlockPosWrapper getWorldPosition(); - -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/chunk/IChunkWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/chunk/IChunkWrapper.java deleted file mode 100644 index 6aaa2a497..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/chunk/IChunkWrapper.java +++ /dev/null @@ -1,52 +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.core.wrapperInterfaces.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.world.IBiomeWrapper; - -/** - * @author James Seibel - * @version 11-17-2021 - */ -public interface IChunkWrapper -{ - int getHeight(); - - boolean isPositionInWater(AbstractBlockPosWrapper blockPos); - - int getHeightMapValue(int xRel, int zRel); - - IBiomeWrapper getBiome(int xRel, int yAbs, int zRel); - - IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos); - - IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos); - - AbstractChunkPosWrapper getPos(); - - boolean isLightCorrect(); - - boolean isWaterLogged(AbstractBlockPosWrapper blockPos); - - int getEmittedBrightness(AbstractBlockPosWrapper blockPos); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java deleted file mode 100644 index 2d0fc46e6..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java +++ /dev/null @@ -1,512 +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.core.wrapperInterfaces.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.FogColorMode; -import com.seibel.lod.core.enums.rendering.FogDistance; -import com.seibel.lod.core.enums.rendering.FogDrawMode; -import com.seibel.lod.core.objects.MinDefaultMax; -import com.seibel.lod.core.util.LodUtil; - -/** - * This holds the config defaults, setters/getters - * that should be hooked into the host mod loader (Fabric, Forge, etc.), and - * the options that should be implemented in a configWrapperSingleton. - * - * @author James Seibel - * @version 12-1-2021 - */ -public interface ILodConfigWrapperSingleton -{ - IClient client(); - - - interface IClient - { - IGraphics graphics(); - IWorldGenerator worldGenerator(); - IAdvanced advanced(); - - - //==================// - // Graphics Configs // - //==================// - interface IGraphics - { - String DESC = "These settings control how the mod will look in game"; - - IQuality quality(); - IFogQuality fogQuality(); - IAdvancedGraphics advancedGraphics(); - - - interface IQuality - { - String DESC = "These settings control how detailed the fake chunks will be."; - - HorizontalResolution DRAW_RESOLUTION_DEFAULT = HorizontalResolution.BLOCK; - String DRAW_RESOLUTION_DESC = "" - + " What is the maximum detail fake chunks should be drawn at? \n" - + " This setting will only affect closer chunks.\n" - + " Higher settings will increase memory and GPU usage. \n" - + "\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 (width of one block). \n" - + "\n" - + " Lowest Quality: " + HorizontalResolution.CHUNK - + " Highest Quality: " + HorizontalResolution.BLOCK; - HorizontalResolution getDrawResolution(); - void setDrawResolution(HorizontalResolution newHorizontalResolution); - - MinDefaultMax LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX = new MinDefaultMax(16, 64, 1024); - String LOD_CHUNK_RENDER_DISTANCE_DESC = "" - + " The radius of the mod's render distance. (measured in chunks) \n"; - int getLodChunkRenderDistance(); - void setLodChunkRenderDistance(int newLodChunkRenderDistance); - - VerticalQuality VERTICAL_QUALITY_DEFAULT = VerticalQuality.MEDIUM; - String VERTICAL_QUALITY_DESC = "" - + " This indicates how detailed fake chunks will represent \n" - + " overhangs, caves, floating islands, ect. \n" - + " Higher options will make the world more accurate, but" - + " will increase memory and GPU usage. \n" - + "\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" - + "\n" - + " Lowest Quality: " + VerticalQuality.LOW - + " Highest Quality: " + VerticalQuality.HIGH; - VerticalQuality getVerticalQuality(); - void setVerticalQuality(VerticalQuality newVerticalQuality); - - MinDefaultMax HORIZONTAL_SCALE_MIN_DEFAULT_MAX = new MinDefaultMax(2, 8, 32); - String HORIZONTAL_SCALE_DESC = "" - + " This indicates how quickly fake chunks decrease in quality the further away they are. \n" - + " Higher settings will render higher quality fake chunks farther away, \n" - + " but will increase memory and GPU usage."; - int getHorizontalScale(); - void setHorizontalScale(int newHorizontalScale); - - HorizontalQuality HORIZONTAL_QUALITY_DEFAULT = HorizontalQuality.MEDIUM; - String HORIZONTAL_QUALITY_DESC = "" - + " This indicates the exponential base of the quadratic drop-off \n" - + "\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" - + "\n" - + " Lowest Quality: " + HorizontalQuality.LOWEST - + " Highest Quality: " + HorizontalQuality.HIGH; - HorizontalQuality getHorizontalQuality(); - void setHorizontalQuality(HorizontalQuality newHorizontalQuality); - } - - interface IFogQuality - { - String DESC = "These settings control the fog quality."; - - FogDistance FOG_DISTANCE_DEFAULT = FogDistance.FAR; - String FOG_DISTANCE_DESC = "" - + " At what distance should Fog be drawn on the fake chunks? \n" - + "\n" - + " This setting shouldn't affect performance."; - FogDistance getFogDistance(); - void setFogDistance(FogDistance newFogDistance); - - FogDrawMode FOG_DRAW_MODE_DEFAULT = FogDrawMode.FOG_ENABLED; - String FOG_DRAW_MODE_DESC = "" - + " When should fog be drawn? \n" - + "\n" - + " " + FogDrawMode.USE_OPTIFINE_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawMode.FOG_ENABLED + ". \n" - + " " + FogDrawMode.FOG_ENABLED + ": Never draw fog on the LODs \n" - + " " + FogDrawMode.FOG_DISABLED + ": Always draw fast fog on the LODs \n" - + "\n" - + " Disabling fog will improve GPU performance."; - FogDrawMode getFogDrawMode(); - void setFogDrawMode(FogDrawMode newFogDrawMode); - - FogColorMode FOG_COLOR_MODE_DEFAULT = FogColorMode.USE_WORLD_FOG_COLOR; - String FOG_COLOR_MODE_DESC = "" - + " What color should fog use? \n" - + "\n" - + " " + FogColorMode.USE_WORLD_FOG_COLOR + ": Use the world's fog color. \n" - + " " + FogColorMode.USE_SKY_COLOR + ": Use the sky's color. \n" - + "\n" - + " This setting doesn't affect performance."; - FogColorMode getFogColorMode(); - void setFogColorMode(FogColorMode newFogColorMode); - - boolean DISABLE_VANILLA_FOG_DEFAULT = false; - String DISABLE_VANILLA_FOG_DESC = "" - + " If true disable Minecraft's fog. \n" - + "\n" - + " Experimental! Will cause issues with Sodium and \n" - + " may not play nice with other mods that edit fog. \n"; - boolean getDisableVanillaFog(); - void setDisableVanillaFog(boolean newDisableVanillaFog); - } - - interface IAdvancedGraphics - { - String DESC = "Graphics options that are a bit more technical."; - - LodTemplate LOD_TEMPLATE_DEFAULT = LodTemplate.CUBIC; - String LOD_TEMPLATE_DESC = "" - + " 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"; - LodTemplate getLodTemplate(); - void setLodTemplate(LodTemplate newLodTemplate); - - boolean DISABLE_DIRECTIONAL_CULLING_DEFAULT = false; - String DISABLE_DIRECTIONAL_CULLING_DESC = "" - + " If false fake chunks behind the player's camera \n" - + " aren't drawn, increasing GPU performance. \n" - + "\n" - + " If true all LODs are drawn, even those behind \n" - + " the player's camera, decreasing GPU performance. \n" - + "\n" - + " Disable this if you see LODs disappearing at the corners of your vision. \n"; - boolean getDisableDirectionalCulling(); - void setDisableDirectionalCulling(boolean newDisableDirectionalCulling); - - boolean ALWAYS_DRAW_AT_MAD_QUALITY_DEFAULT = false; - String ALWAYS_DRAW_AT_MAD_QUALITY_DESC = "" - + " Disable quality falloff, \n" - + " all fake chunks will be drawn at the highest \n" - + " available detail level. \n" - + "\n" - + " WARNING: \n" - + " This could cause an Out Of Memory crash when using render \n" - + " distances higher than 128 and will drastically increase GPU usage. \n"; - boolean getAlwaysDrawAtMaxQuality(); - void setAlwaysDrawAtMaxQuality(boolean newAlwaysDrawAtMaxQuality); - - VanillaOverdraw VANILLA_OVERDRAW_DEFAULT = VanillaOverdraw.DYNAMIC; - String VANILLA_OVERDRAW_DESC = "" - + " 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 + " will be used depending on the dimension. \n" - + " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing holes in the world. \n" - + "\n" - + " This setting shouldn't affect performance. \n"; - VanillaOverdraw getVanillaOverdraw(); - void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw); - - boolean USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT = false; - String USE_EXTENDED_NEAR_CLIP_PLANE_DESC = "" - + " Will prevent some overdraw issues, but may cause nearby fake chunks to render incorrectly \n" - + " especially when in/near an ocean. \n" - + "\n" - + " This setting shouldn't affect performance. \n"; - boolean getUseExtendedNearClipPlane(); - void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane); - } - } - - - - - //========================// - // WorldGenerator Configs // - //========================// - interface IWorldGenerator - { - String DESC = "These settings control how fake chunks outside your normal view range are generated."; - - GenerationPriority GENERATION_PRIORITY_DEFAULT = GenerationPriority.AUTO; - String GENERATION_PRIORITY_DESC = "" - + " In what order should fake chunks be generated outside the vanilla render distance? \n" - + "\n" - + " " + GenerationPriority.FAR_FIRST + " \n" - + " Fake chunks are generated from lowest to highest detail \n" - + " with a small priority for far away regions. \n" - + " This fills in the world fastest, but you will have large low detail \n" - + " blocks for a while while the generation happens. \n" - + "\n" - + " " + GenerationPriority.NEAR_FIRST + " \n" - + " Fake chunks are generated around the player \n" - + " in a spiral, similar to vanilla minecraft. \n" - + " Best used when on a server since we can't generate \n" - + " fake chunks. \n" - + "\n" - + " " + GenerationPriority.AUTO + " \n" - + " Uses " + GenerationPriority.FAR_FIRST + " when on a single player world \n" - + " and " + GenerationPriority.NEAR_FIRST + " when connected to a server. \n" - + "\n" - + " This shouldn't affect performance."; - GenerationPriority getGenerationPriority(); - void setGenerationPriority(GenerationPriority newGenerationPriority); - - DistanceGenerationMode DISTANCE_GENERATION_MODE_DEFAULT = DistanceGenerationMode.SURFACE; - String DISTANCE_GENERATION_MODE_DESC = "" - + " How detailed should fake chunks be generated outside the vanilla render distance? \n" - + "\n" - + " " + DistanceGenerationMode.NONE + " \n" - + " Don't run the distance generator. \n" - + " No CPU usage - Fastest \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 " + DistanceGenerationMode.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.FULL + " \n" - + " Ask the local 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" - + "\n" - + " The multithreaded options may increase CPU load significantly (while generating) \n" - + " depending on how many world generation threads you have allocated. \n"; - DistanceGenerationMode getDistanceGenerationMode(); - void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode); - - boolean ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT = false; - String ALLOW_UNSTABLE_FEATURE_GENERATION_DESC = "" - + " 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"; - boolean getAllowUnstableFeatureGeneration(); - void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration); - - BlocksToAvoid BLOCKS_TO_AVOID_DEFAULT = BlocksToAvoid.BOTH; - String BLOCKS_TO_AVOID_DESC = "" - + " When generating fake chunks, what blocks should be ignored? \n" - + " Ignored blocks don't affect the height of the fake chunk, but might affect the color. \n" - + " So using " + BlocksToAvoid.BOTH + " will prevent snow covered blocks from appearing one block too tall, \n" - + " but will still show the snow's color.\n" - + "\n" - + " " + BlocksToAvoid.NONE + ": Use all blocks when generating fake chunks \n" - + " " + BlocksToAvoid.NON_FULL + ": Only use full blocks when generating fake chunks (ignores slabs, lanterns, torches, tall grass, etc.) \n" - + " " + BlocksToAvoid.NO_COLLISION + ": Only use solid blocks when generating fake chunks (ignores tall grass, torches, etc.) \n" - + " " + BlocksToAvoid.BOTH + ": Only use full solid blocks when generating fake chunks \n" - + "\n" - + " This wont't affect performance."; - BlocksToAvoid getBlocksToAvoid(); - void setBlockToAvoid(BlocksToAvoid newBlockToAvoid); - } - - - - - //============================// - // AdvancedModOptions Configs // - //============================// - interface IAdvanced - { - String DESC = "Advanced mod settings"; - - IThreading threading(); - IDebugging debugging(); - IBuffers buffers(); - - - interface IThreading - { - String DESC = "These settings control how many CPU threads the mod uses for different tasks."; - - MinDefaultMax NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT - = new MinDefaultMax(1, - Runtime.getRuntime().availableProcessors() / 2, - Runtime.getRuntime().availableProcessors()); - String NUMBER_OF_WORLD_GENERATION_THREADS_DESC = "" - + " How many threads should be used when generating fake chunks outside \n" - + " the normal render distance? \n" - + "\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" - + " This and the number of buffer builder threads are independent, \n" - + " so if they add up to more threads than your CPU has cores, \n" - + " that shouldn't cause an issue. \n" - + "\n" - + " The maximum value is the number of logical processors on your CPU. \n" - + " Requires a restart to take effect. \n"; - int getNumberOfWorldGenerationThreads(); - void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads); - - MinDefaultMax NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX - = new MinDefaultMax(1, - Runtime.getRuntime().availableProcessors() / 2, - Runtime.getRuntime().availableProcessors()); - String NUMBER_OF_BUFFER_BUILDER_THREADS_DESC = "" - + " How many threads are used when building vertex buffers? \n" - + " (The things sent to your GPU to draw the fake chunks). \n" - + "\n" - + " If you experience high CPU usage when NOT generating distant \n" - + " fake chunks, lower this number. A higher number will make fake\n" - + " fake chunks' transition faster when moving around the world. \n" - + "\n" - + " This and the number of world generator threads are independent, \n" - + " so if they add up to more threads than your CPU has cores, \n" - + " that shouldn't cause an issue. \n" - + "\n" - + " The maximum value is the number of logical processors on your CPU. \n" - + " Requires a restart to take effect. \n"; - int getNumberOfBufferBuilderThreads(); - void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads); - } - - interface IDebugging - { - String DESC = "These settings can be used to look for bugs, or see how certain aspects of the mod work."; - - boolean DRAW_LODS_DEFAULT = true; - String DRAW_LODS_DESC = "" - + " 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" - + "\n" - + " Disabling rendering will reduce GPU usage \n"; - boolean getDrawLods(); - void setDrawLods(boolean newDrawLods); - - DebugMode DEBUG_MODE_DEFAULT = DebugMode.OFF; - String DEBUG_MODE_DESC = "" - + " Should specialized colors/rendering modes be used? \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"; - DebugMode getDebugMode(); - void setDebugMode(DebugMode newDebugMode); - - boolean DEBUG_KEYBINDINGS_ENABLED_DEFAULT = true; - String DEBUG_KEYBINDINGS_ENABLED_DESC = "" - + " 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."; - boolean getDebugKeybindingsEnabled(); - void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings); - } - - interface IBuffers - { - String DESC = "These settings affect how often geometry is rebuilt."; - - GpuUploadMethod GPU_UPLOAD_METHOD_DEFAULT = GpuUploadMethod.AUTO; - String GPU_UPLOAD_METHOD_DESC = "" - + " What method should be used to upload geometry to the GPU? \n" - + "\n" - + " " + GpuUploadMethod.AUTO + ": Picks the best option based on the GPU you have. \n" - + " " + GpuUploadMethod.BUFFER_STORAGE + ": Default for NVIDIA if OpenGL 4.5 is supported. \n" - + " Fast rendering, no stuttering. \n" - + " " + GpuUploadMethod.SUB_DATA + ": Backup option for NVIDIA. \n" - + " Fast rendering but may stutter when uploading. \n" - + " " + GpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. Possibly the best option for integrated GPUs. \n" - + " Default option for AMD/Intel. \n" - + " May end up storing buffers in System memory. \n" - + " Fast rending if in GPU memory, slow if in system memory, \n" - + " but won't stutter when uploading. \n" - + " " + GpuUploadMethod.DATA + ": Fast rendering but will stutter when uploading. \n" - + " Backup option for AMD/Intel. \n" - + " Fast rendering but may stutter when uploading. \n" - + "\n" - + " If you don't see any difference when changing these settings, or the world looks corrupted: \n" - + " Restart the game to clear the old buffers. \n"; - GpuUploadMethod getGpuUploadMethod(); - void setGpuUploadMethod(GpuUploadMethod newGpuUploadMethod); - - MinDefaultMax GPU_UPLOAD_TIMEOUT_IN_MILLISECONDS_DEFAULT = new MinDefaultMax(0, 0, 5000); - String GPU_UPLOAD_TIMEOUT_IN_MILLISECONDS_DESC = "" - + " How long should we wait before uploading a buffer to the GPU? \n" - + " Helpful resource for frame times: https://fpstoms.com \n" - + "\n" - + " Longer times may reduce stuttering but will make fake chunks \n" - + " transition and load slower. \n" - + "\n" - + " NOTE:\n" - + " This should be a last resort option." - + " Only change this from [0], after you have tried all of the \n" - + " \"GPU Upload methods\" and determined even the best stutters with yoru hardware."; - int getGpuUploadTimeoutInMilliseconds(); - void setGpuUploadTimeoutInMilliseconds(int newTimeoutInMilliseconds); - - String REBUILD_TIMES_DESC = "" - + " How frequently should vertex buffers (geometry) be rebuilt and sent to the GPU? \n" - + " Higher settings may cause stuttering, but will prevent holes in the world \n"; - BufferRebuildTimes REBUILD_TIMES_DEFAULT = BufferRebuildTimes.NORMAL; - BufferRebuildTimes getRebuildTimes(); - void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes); - } - } - } - -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java deleted file mode 100644 index b39289b1a..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java +++ /dev/null @@ -1,69 +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.core.wrapperInterfaces.minecraft; - -import java.awt.Color; -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; - -/** - * Contains everything related to - * rendering in Minecraft. - * - * @author James Seibel - * @version 11-26-2021 - */ -public interface IMinecraftRenderWrapper -{ - Vec3f getLookAtVector(); - - AbstractBlockPosWrapper getCameraBlockPosition(); - - boolean playerHasBlindnessEffect(); - - Vec3d getCameraExactPosition(); - - Mat4f getDefaultProjectionMatrix(float partialTicks); - - double getGamma(); - - Color getFogColor(); - - Color getSkyColor(); - - double getFov(float partialTicks); - - /** Measured in chunks */ - int getRenderDistance(); - - int getScreenWidth(); - int getScreenHeight(); - - /** - * This method returns the ChunkPos of all chunks that Minecraft - * is going to render this frame. - */ - HashSet getRenderedChunks(); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftWrapper.java deleted file mode 100644 index f6668e0dc..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IMinecraftWrapper.java +++ /dev/null @@ -1,157 +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.core.wrapperInterfaces.minecraft; - -import java.awt.Color; -import java.io.File; -import java.util.ArrayList; - -import com.seibel.lod.core.enums.LodDirection; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - -/** - * Contains everything related to the Minecraft object. - * - * @author James Seibel - * @version 9-16-2021 - */ -public interface IMinecraftWrapper -{ - //================// - // helper methods // - //================// - - /** - * This should be called at the beginning of every frame to - * clear any Minecraft data that becomes out of date after a frame.

- *

- * LightMaps and other time sensitive objects fall in this category.

- *

- * This doesn't affect OpenGL objects in any way. - */ - void clearFrameObjectCache(); - - - - //=================// - // method wrappers // - //=================// - - float getShade(LodDirection lodDirection); - - boolean hasSinglePlayerServer(); - - String getCurrentServerName(); - String getCurrentServerIp(); - String getCurrentServerVersion(); - - /** Returns the dimension the player is currently in */ - IDimensionTypeWrapper getCurrentDimension(); - - String getCurrentDimensionId(); - - /** This texture changes every frame */ - ILightMapWrapper getCurrentLightMap(); - - /** - * Returns the color int at the given pixel coordinates - * from the current lightmap. - * @param u x location in texture space - * @param v z location in texture space - */ - int getColorIntFromLightMap(int u, int v); - - /** - * Returns the Color at the given pixel coordinates - * from the current lightmap. - * @param u x location in texture space - * @param v z location in texture space - */ - Color getColorFromLightMap(int u, int v); - - - - - //=============// - // Simple gets // - //=============// - - boolean playerExists(); - - AbstractBlockPosWrapper getPlayerBlockPos(); - - AbstractChunkPosWrapper getPlayerChunkPos(); - - /** - * Attempts to get the ServerWorld for the dimension - * the user is currently in. - * @returns null if no ServerWorld is available - */ - IWorldWrapper getWrappedServerWorld(); - - IWorldWrapper getWrappedClientWorld(); - - File getGameDirectory(); - - IProfilerWrapper getProfiler(); - - float getSkyDarken(float partialTicks); - - boolean connectedToServer(); - - /** Returns all worlds available to the server */ - ArrayList getAllServerWorlds(); - - - - void sendChatMessage(String string); - - /** - * Crashes Minecraft, displaying the given errorMessage

- * In the following format:
- * - * The game crashed whilst errorMessage
- * Error: ExceptionClass: exceptionErrorMessage
- * Exit Code: -1
- */ - void crashMinecraft(String errorMessage, Throwable exception); - - - - - - - - - - - - - - - - - - -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IProfilerWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IProfilerWrapper.java deleted file mode 100644 index bf081fb72..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/minecraft/IProfilerWrapper.java +++ /dev/null @@ -1,33 +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.core.wrapperInterfaces.minecraft; - -/** - * @author James Seibel - * @version 11-20-2021 - */ -public interface IProfilerWrapper -{ - void push(String newSection); - - void popPush(String newSection); - - void pop(); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/misc/ILightMapWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/misc/ILightMapWrapper.java deleted file mode 100644 index 76453b5b8..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/misc/ILightMapWrapper.java +++ /dev/null @@ -1,29 +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.core.wrapperInterfaces.misc; - -/** - * @author James Seibel - * @version 11-20-2021 - */ -public interface ILightMapWrapper -{ - int getLightValue(int skyLight, int blockLight); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IBiomeColorWrapperSingleton.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IBiomeColorWrapperSingleton.java deleted file mode 100644 index 76a1c1768..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IBiomeColorWrapperSingleton.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.core.wrapperInterfaces.world; - -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; - - -/** - * Contains everything related to biome colors. - * - * @author James Seibel - * @version 11-15-2021 - */ -public interface IBiomeColorWrapperSingleton -{ - IBiomeColorWrapperSingleton getInstance(); - - int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos); - int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos); - int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IBiomeWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IBiomeWrapper.java deleted file mode 100644 index eb1b620f6..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IBiomeWrapper.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.core.wrapperInterfaces.world; - -/** - * @author James Seibel - * @version 11-15-2021 - */ -public interface IBiomeWrapper -{ - /** Returns a color int for the given biome. */ - int getColorForBiome(int x, int z); - - int getGrassTint(int x, int z); - - int getFolliageTint(); - - int getWaterTint(); - -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IDimensionTypeWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IDimensionTypeWrapper.java deleted file mode 100644 index cbafd5cd6..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IDimensionTypeWrapper.java +++ /dev/null @@ -1,33 +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.core.wrapperInterfaces.world; - -/** - * @author James Seibel - * @version 11-15-2021 - */ -public interface IDimensionTypeWrapper -{ - String getDimensionName(); - - boolean hasCeiling(); - - boolean hasSkyLight(); -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IWorldWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IWorldWrapper.java deleted file mode 100644 index cb35e1fd5..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/world/IWorldWrapper.java +++ /dev/null @@ -1,61 +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.core.wrapperInterfaces.world; - -import java.io.File; - -import com.seibel.lod.core.enums.WorldType; -import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; - -/** - * Can be either a Server world or a Client world. - * - * @author James Seibel - * @version 11-20-2021 - */ -public interface IWorldWrapper -{ - IDimensionTypeWrapper getDimensionType(); - - WorldType getWorldType(); - - int getBlockLight(AbstractBlockPosWrapper blockPos); - - int getSkyLight(AbstractBlockPosWrapper blockPos); - - IBiomeWrapper getBiome(AbstractBlockPosWrapper blockPos); - - boolean hasCeiling(); - - boolean hasSkyLight(); - - // Pls don't use this - // If the world is null then this can't be called and gives an error - boolean isEmpty(); - - int getHeight(); - - int getSeaLevel(); - - /** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */ - File getSaveFolder() throws UnsupportedOperationException; - - -} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java deleted file mode 100644 index 696c89e86..000000000 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java +++ /dev/null @@ -1,50 +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.core.wrapperInterfaces.worldGeneration; - -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; - -/** - * This is used for generating chunks - * in a variety of detail and threading levels. - *

- * Abstract instead of an interface, so - * we can define its constructors. - * - * @author James Seibel - * @version 11-20-2021 - */ -public abstract class AbstractWorldGeneratorWrapper -{ - public AbstractWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { } - - - public abstract void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode); - - public abstract void generateSurface(AbstractChunkPosWrapper pos); - - public abstract void generateFeatures(AbstractChunkPosWrapper pos); - - public abstract void generateFull(AbstractChunkPosWrapper pos); -} diff --git a/src/main/resources/shaders/flat_shaded.frag b/src/main/resources/shaders/flat_shaded.frag deleted file mode 100644 index 7677dfa66..000000000 --- a/src/main/resources/shaders/flat_shaded.frag +++ /dev/null @@ -1,89 +0,0 @@ -#version 150 core - -in vec4 vertexColor; -in vec4 vertexWorldPos; -//in vec2 textureCoord; - - -out vec4 fragColor; - - -//uniform sampler2D texImage; -uniform vec3 cameraPos; - -uniform bool fogEnabled; -uniform bool nearFogEnabled; -uniform bool farFogEnabled; - -uniform float nearFogStart; -uniform float nearFogEnd; -uniform float farFogStart; -uniform float farFogEnd; -uniform vec4 fogColor; - - -// method definitions -float getFogAlpha(float start, float end, float dist); - - - -/** - * Fragment Shader - * - * author: James Seibel - * version: 11-26-2021 - */ -void main() -{ - // TODO: add a white texture to support Optifine shaders - //vec4 textureColor = texture(texImage, textureCoord); - //fragColor = vertexColor * textureColor; - - - vec4 returnColor; - if (fogEnabled) - { - // add fog - - float dist = distance(vertexWorldPos, vec4(cameraPos,1)); - // no fog by default - float fogAlpha = 0; - - // less than because nearFogStart is farther away than nearFogEnd - if (nearFogEnabled && dist < nearFogStart) - { - fogAlpha = getFogAlpha(nearFogStart, nearFogEnd, dist); - } - else if (farFogEnabled) - { - fogAlpha = getFogAlpha(farFogStart, farFogEnd, dist); - } - - returnColor = mix(vertexColor, vec4(fogColor.xyz, 1), fogAlpha); - } - else - { - // simple flat color - returnColor = vertexColor; - } - - - - fragColor = returnColor; -} - - - - -/** - * Returns the fog strength for the given fragment. - * This is the same implementation as legacy OpenGL's Linear fog option. - * 1 = completely opaque fog - * 0 = no fog - */ -float getFogAlpha(float start, float end, float dist) -{ - float fogAlpha = 1 - ((end - dist) / (end - start)); - return clamp(fogAlpha, 0, 1); -} - diff --git a/src/main/resources/shaders/standard.vert b/src/main/resources/shaders/standard.vert deleted file mode 100644 index 32d457ac6..000000000 --- a/src/main/resources/shaders/standard.vert +++ /dev/null @@ -1,34 +0,0 @@ -#version 150 core - -in vec3 vPosition; -in vec4 color; - - -out vec4 vertexColor; -out vec4 vertexWorldPos; -//out vec2 textureCoord; -out float depth; - - -uniform mat4 modelViewMatrix; -uniform mat4 projectionMatrix; - - -/** - * Vertex Shader - * - * author: James Seibel - * version: 11-26-2021 - */ -void main() -{ - // TODO: add a simple white texture to support Optifine shaders - //textureCoord = textureCoord; - - vertexColor = color; - vertexWorldPos = vec4(vPosition, 1); - - // the vPosition needs to be converted to a vec4 so it can be multiplied - // by the 4x4 matrices - gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1); -}