diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java index 45bc7231e..0ada1d11b 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java @@ -56,23 +56,20 @@ import com.seibel.lod.util.LodThreadFactory; import com.seibel.lod.util.LodUtil; import com.seibel.lod.util.ThreadMapUtil; import com.seibel.lod.wrappers.MinecraftWrapper; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; +import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.vertex.VertexBuffer; -import net.minecraft.client.world.ClientWorld; import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.LightType; /** * This object is used to create NearFarBuffer objects. * @author James Seibel - * @version 10-23-2021 + * @version 11-8-2021 */ public class LodBufferBuilder { - private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; /** The thread used to generate new LODs off the main thread. */ public static final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilder.class.getSimpleName() + " - main")); @@ -141,11 +138,11 @@ public class LodBufferBuilder private volatile RegionPos center; /** - * This is the ChunkPos the player was at the last time the buffers were built. + * 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 ChunkPos drawableCenterChunkPos = new ChunkPos(0, 0); - private volatile ChunkPos buildableCenterChunkPos = new ChunkPos(0, 0); + private volatile ChunkPosWrapper drawableCenterChunkPos = new ChunkPosWrapper(0, 0); + private volatile ChunkPosWrapper buildableCenterChunkPos = new ChunkPosWrapper(0, 0); @@ -167,7 +164,7 @@ public class LodBufferBuilder * swapped with the drawable buffers in the LodRenderer to be drawn. */ public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim, - BlockPos playerBlockPos, boolean fullRegen) + BlockPosWrapper playerBlockPos, boolean fullRegen) { // only allow one generation process to happen at a time @@ -190,15 +187,15 @@ public class LodBufferBuilder // 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, - BlockPos playerBlockPos, boolean fullRegen) + BlockPosWrapper playerBlockPos, boolean fullRegen) { bufferLock.lock(); try { // round the player's block position down to the nearest chunk BlockPos - ChunkPos playerChunkPos = new ChunkPos(playerBlockPos); - BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition(); + ChunkPosWrapper playerChunkPos = new ChunkPosWrapper(playerBlockPos); + BlockPosWrapper playerBlockPosRounded = playerChunkPos.getWorldPosition(); //long startTime = System.currentTimeMillis(); @@ -232,8 +229,7 @@ public class LodBufferBuilder // create the nodeToRenderThreads // //================================// - ClientWorld world = mc.getClientWorld(); - skyLightPlayer = world.getBrightness(LightType.SKY, playerBlockPos); + skyLightPlayer = MinecraftWrapper.INSTANCE.getWrappedClientLevel().getSkyLight(playerBlockPos); for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++) { @@ -315,8 +311,8 @@ public class LodBufferBuilder posX = posToRender.getNthPosX(index); posZ = posToRender.getNthPosZ(index); - int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x; - int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z; + 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 @@ -343,8 +339,8 @@ public class LodBufferBuilder xAdj = posX + Box.DIRECTION_NORMAL_MAP.get(direction).getX(); zAdj = posZ + Box.DIRECTION_NORMAL_MAP.get(direction).getZ(); long data; - chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.x; - chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.z; + 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 @@ -476,13 +472,13 @@ public class LodBufferBuilder } } - private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, ChunkPos playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){ + private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, ChunkPosWrapper playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){ // skip any chunks that Minecraft is going to render - int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x; - int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z; - + int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX(); + int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ(); + // check if the chunk is on the border boolean isItBorderPos; if (LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get() == VanillaOverdraw.BORDER) @@ -519,13 +515,16 @@ public class LodBufferBuilder */ public void setupBuffers(LodDimension lodDimension) { - GlProxy glProxy = GlProxy.getInstance(); - 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 BufferBuilder[numbRegionsWide][numbRegionsWide][]; @@ -616,6 +615,7 @@ public class LodBufferBuilder } } + glProxy.setGlContext(oldContext); bufferLock.unlock(); } @@ -778,7 +778,11 @@ public class LodBufferBuilder for (int i = 0; i < buildableBuffers[x][z].length; i++) { ByteBuffer uploadBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond(); - vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, true, uploadMethod); + int storageBufferId = 0; + if (buildableStorageBufferIds != null) + storageBufferId = buildableStorageBufferIds[x][z][i]; + + vboUpload(buildableVbos[x][z][i], storageBufferId, uploadBuffer, true, uploadMethod); lodDim.setRegenRegionBufferByArrayIndex(x, z, false); } } @@ -793,7 +797,7 @@ public class LodBufferBuilder } finally { - GL11.glFinish(); + GL15.glFinish(); // close the context so it can be re-used later. // I'm guessing we can't just leave it because the executor service @@ -810,14 +814,13 @@ public class LodBufferBuilder if (vbo.id != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER) { // this is how many points will be rendered - vbo.vertexCount = (uploadBuffer.capacity() / vbo.format.getVertexSize()); - + 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) + 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); @@ -898,7 +901,6 @@ public class LodBufferBuilder // hybrid subData/bufferData // // less stutter, low GPU usage - //long size = GL31.glGetBufferParameteri64(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); // hopefully just a int should be long enough long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER) { @@ -953,9 +955,9 @@ public class LodBufferBuilder { public final VertexBuffer[][][] vbos; public final int[][][] storageBufferIds; - public final ChunkPos drawableCenterChunkPos; + public final ChunkPosWrapper drawableCenterChunkPos; - public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, ChunkPos newDrawableCenterChunkPos) + public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, ChunkPosWrapper newDrawableCenterChunkPos) { vbos = newVbos; storageBufferIds = newStorageBufferIds; diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java index daff1fa0f..0cf1eab1d 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/AbstractLodTemplate.java @@ -23,10 +23,10 @@ import java.util.Map; import com.seibel.lod.enums.DebugMode; import com.seibel.lod.util.ColorUtil; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; /** * This is the abstract class used to create different @@ -38,7 +38,7 @@ public abstract class AbstractLodTemplate { /** Uploads the given LOD to the buffer. */ - public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map adjData, + public abstract void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map adjData, byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled); /** add the given position and color to the buffer */ diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/Box.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/Box.java index 90172d0a7..acea09391 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/Box.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/Box.java @@ -28,10 +28,10 @@ import com.seibel.lod.enums.DebugMode; import com.seibel.lod.util.ColorUtil; import com.seibel.lod.util.DataPointUtil; import com.seibel.lod.util.LodUtil; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; import com.seibel.lod.wrappers.MinecraftWrapper; import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.vector.Vector3i; /** @@ -74,35 +74,58 @@ public class Box public static final Map DIRECTION_VERTEX_MAP = new HashMap() {{ put(Direction.UP, new int[][] { - { 0, 1, 0 }, - { 0, 1, 1 }, - { 1, 1, 1 }, - { 1, 1, 0 } }); + { 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(Direction.DOWN, new int[][] { - { 1, 0, 0 }, - { 1, 0, 1 }, - { 0, 0, 1 }, - { 0, 0, 0 } }); + { 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(Direction.EAST, new int[][] { - { 1, 1, 0 }, - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 0, 0 } }); + { 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(Direction.WEST, new int[][] { - { 0, 0, 0 }, - { 0, 0, 1 }, - { 0, 1, 1 }, - { 0, 1, 0 } }); + { 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(Direction.SOUTH, new int[][] { - { 1, 0, 1 }, - { 1, 1, 1 }, - { 0, 1, 1 }, - { 0, 0, 1 } }); + { 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(Direction.NORTH, new int[][] { - { 0, 0, 0 }, - { 0, 1, 0 }, - { 1, 1, 0 }, - { 1, 0, 0 } }); + { 0, 0, 0 }, // 0 + { 0, 1, 0 }, // 1 + { 1, 1, 0 }, // 2 + + { 0, 0, 0 }, // 0 + { 1, 1, 0 }, // 2 + { 1, 0, 0 } // 3 + }); }}; @@ -226,7 +249,7 @@ public class Box for (Direction direction : DIRECTIONS) { if (!adjShadeDisabled[DIRECTION_INDEX.get(direction)]) - colorMap[DIRECTION_INDEX.get(direction)] = ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true)); + colorMap[DIRECTION_INDEX.get(direction)] = ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientLevel().getShade(direction, true)); else colorMap[DIRECTION_INDEX.get(direction)] = color; } @@ -241,7 +264,7 @@ public class Box if (LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get() != DebugMode.SHOW_DETAIL) return colorMap[DIRECTION_INDEX.get(direction)]; else - return ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true)); + return ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientLevel().getShade(direction, true)); } /** @@ -279,7 +302,7 @@ public class Box } /** determine which faces should be culled */ - public void setUpCulling(int cullingDistance, BlockPos playerPos) + public void setUpCulling(int cullingDistance, BlockPosWrapper playerPos) { for (Direction direction : DIRECTIONS) { diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java index 677dc7c7d..8faae6538 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/CubicLodTemplate.java @@ -25,15 +25,16 @@ import com.seibel.lod.enums.DebugMode; import com.seibel.lod.util.ColorUtil; import com.seibel.lod.util.DataPointUtil; import com.seibel.lod.util.LodUtil; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; + import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; /** * Builds LODs as rectangular prisms. * @author James Seibel - * @version 10-10-2021 + * @version 11-8-2021 */ public class CubicLodTemplate extends AbstractLodTemplate { @@ -44,7 +45,7 @@ public class CubicLodTemplate extends AbstractLodTemplate } @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map adjData, byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled) { if (box == null) @@ -79,7 +80,7 @@ public class CubicLodTemplate extends AbstractLodTemplate private void generateBoundingBox(Box box, int height, int depth, int width, double xOffset, double yOffset, double zOffset, - BlockPos bufferCenterBlockPos, + BlockPosWrapper bufferCenterBlockPos, Map adjData, int color, int skyLight, @@ -118,10 +119,11 @@ public class CubicLodTemplate extends AbstractLodTemplate { if(box.isCulled(direction)) continue; + int verticalFaceIndex = 0; while (box.shouldRenderFace(direction, verticalFaceIndex)) { - for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++) + for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++) { color = box.getColor(direction); skyLight = box.getSkyLight(direction, verticalFaceIndex); diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java index dd88744ab..e3f92f067 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/DynamicLodTemplate.java @@ -24,9 +24,9 @@ import java.util.Map; import com.seibel.lod.enums.DebugMode; import com.seibel.lod.proxy.ClientProxy; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; /** * TODO DynamicLodTemplate @@ -39,7 +39,7 @@ import net.minecraft.util.math.BlockPos; public class DynamicLodTemplate extends AbstractLodTemplate { @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map adjData, byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled) { ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!"); diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java index 765399455..2bd3491e1 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/lodTemplates/TriangularLodTemplate.java @@ -24,9 +24,9 @@ import java.util.Map; import com.seibel.lod.enums.DebugMode; import com.seibel.lod.proxy.ClientProxy; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.Direction; -import net.minecraft.util.math.BlockPos; /** * TODO #21 TriangularLodTemplate @@ -37,7 +37,7 @@ import net.minecraft.util.math.BlockPos; public class TriangularLodTemplate extends AbstractLodTemplate { @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map adjData, byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled) { ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!"); diff --git a/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java b/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java index c11a7ed2a..a76b768ae 100644 --- a/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java +++ b/src/main/java/com/seibel/lod/builders/lodBuilding/LodBuilder.java @@ -41,9 +41,8 @@ import com.seibel.lod.wrappers.Block.BlockPosWrapper; import com.seibel.lod.wrappers.Block.BlockShapeWrapper; import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; import com.seibel.lod.wrappers.Chunk.ChunkWrapper; -import com.seibel.lod.wrappers.World.BiomeColorWrapper; import com.seibel.lod.wrappers.World.BiomeWrapper; -import com.seibel.lod.wrappers.World.WorldWrapper; +import com.seibel.lod.wrappers.World.LevelWrapper; import net.minecraft.world.DimensionType; import net.minecraft.world.IWorld; @@ -109,7 +108,7 @@ public class LodBuilder { // we need a loaded client world in order to // get the textures for blocks - if (mc.getClientWorld() == null) + if (mc.getClientLevel() == null) return; // don't try to generate LODs if the user isn't in the world anymore @@ -171,7 +170,7 @@ public class LodBuilder return; // this happens if a LOD is generated after the user leaves the world. - if (MinecraftWrapper.INSTANCE.getWrappedClientWorld() == null) + if (MinecraftWrapper.INSTANCE.getWrappedClientLevel() == null) return; // determine how many LODs to generate horizontally @@ -231,8 +230,8 @@ public class LodBuilder int xAbs; int yAbs; int zAbs; - boolean hasCeiling = mc.getClientWorld().dimensionType().hasCeiling(); - boolean hasSkyLight = mc.getClientWorld().dimensionType().hasSkyLight(); + boolean hasCeiling = mc.getClientLevel().dimensionType().hasCeiling(); + boolean hasSkyLight = mc.getClientLevel().dimensionType().hasSkyLight(); boolean isDefault; BlockPosWrapper blockPos = new BlockPosWrapper(); int index; @@ -387,7 +386,7 @@ public class LodBuilder // 1 means the lighting is a guess int isDefault = 0; - WorldWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerWorld(); + LevelWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerLevel(); int blockBrightness = chunk.getEmittedBrightness(blockPos); // get the air block above or below this block @@ -415,7 +414,7 @@ public class LodBuilder { // we are on predicted terrain, and we don't know what the light here is, // lets just take a guess - if (blockPos.getY() >= mc.getClientWorld().getSeaLevel() - 5) + if (blockPos.getY() >= mc.getClientLevel().getSeaLevel() - 5) { skyLight = 12; isDefault = 1; @@ -426,7 +425,7 @@ public class LodBuilder } else { - world = MinecraftWrapper.INSTANCE.getWrappedClientWorld(); + world = MinecraftWrapper.INSTANCE.getWrappedClientLevel(); if (world.isEmpty()) return 0; // client world sky light (almost never accurate) @@ -448,7 +447,7 @@ public class LodBuilder { // we don't know what the light here is, // lets just take a guess - if (blockPos.getY() >= mc.getClientWorld().getSeaLevel() - 5) + if (blockPos.getY() >= mc.getClientLevel().getSeaLevel() - 5) { skyLight = 12; isDefault = 1; @@ -485,10 +484,7 @@ public class LodBuilder BlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos); if (chunk.isWaterLogged(blockPos)) - { - BiomeWrapper biome = chunk.getBiome(xRel, y, zRel); - return biome.getWaterTint(); - } + blockColorWrapper = BlockColorWrapper.getWaterColor(); else blockColorWrapper = chunk.getBlockColorWrapper(blockPos); @@ -500,20 +496,16 @@ public class LodBuilder if (blockColorWrapper.hasTint()) { - WorldWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerWorld(); - - if (world == null || world.isEmpty()) - world = MinecraftWrapper.INSTANCE.getWrappedClientWorld(); - + BiomeWrapper biome = chunk.getBiome(xRel, y, zRel); int tintValue; if (blockColorWrapper.hasGrassTint()) // grass and green plants - tintValue = BiomeColorWrapper.getGrassColor(world, blockPos); + tintValue = biome.getGrassTint(0,0); else if (blockColorWrapper.hasFolliageTint()) - tintValue = BiomeColorWrapper.getFoliageColor(world, blockPos); + tintValue = biome.getFolliageTint(); else //we can reintroduce this with the wrappers - tintValue = BiomeColorWrapper.getWaterColor(world, blockPos); + tintValue = biome.getWaterTint(); colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, colorOfBlock); } diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodGenWorker.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodGenWorker.java index 4fed6fb9b..63ce0c0df 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodGenWorker.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodGenWorker.java @@ -37,10 +37,10 @@ import com.seibel.lod.objects.LodDimension; import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.util.LodUtil; +import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; import com.seibel.lod.wrappers.Chunk.ChunkWrapper; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.util.math.ChunkPos; import net.minecraft.util.palette.UpgradeData; import net.minecraft.util.registry.Registry; import net.minecraft.world.biome.Biome; @@ -90,7 +90,7 @@ public class LodGenWorker implements IWorker - public LodGenWorker(ChunkPos newPos, DistanceGenerationMode newGenerationMode, + public LodGenWorker(ChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode, LodBuilder newLodBuilder, LodDimension newLodDimension, ServerWorld newServerWorld) { @@ -161,9 +161,9 @@ public class LodGenWorker implements IWorker public final DistanceGenerationMode generationMode; public final LodBuilder lodBuilder; - private final ChunkPos pos; + private final ChunkPosWrapper pos; - public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode, + public LodChunkGenThread(ChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode, LodBuilder newLodBuilder, LodDimension newLodDimension, ServerWorld newServerWorld) { @@ -177,13 +177,13 @@ public class LodGenWorker implements IWorker @Override public void run() { - try - { + //try + //{ // only generate LodChunks if they can // be added to the current LodDimension /* TODO I must disable this 'if', if I will find a way to replace it */ - if (lodDim.regionIsInRange(pos.x / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.z / LodUtil.REGION_WIDTH_IN_CHUNKS)) + if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS)) { // //{ @@ -270,20 +270,20 @@ public class LodGenWorker implements IWorker // System.out.println(endTime - startTime); }// if in range - } - catch (Exception e) - { - ClientProxy.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage()); - e.printStackTrace(); - } - finally - { + //} + //catch (Exception e) + //{ + // ClientProxy.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage()); + // e.printStackTrace(); + //} + //finally + //{ // decrement how many threads are running LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1); // this position is no longer being generated LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos); - } + //} }// run @@ -295,7 +295,7 @@ public class LodGenWorker implements IWorker private void generateUsingBiomesOnly() { List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY); + ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY); chunkList.add(chunk); ServerChunkProvider chunkSource = serverWorld.getChunkSource(); @@ -407,7 +407,7 @@ public class LodGenWorker implements IWorker private void generateUsingSurface() { List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY); + ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY); chunkList.add(chunk); LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); @@ -447,7 +447,7 @@ public class LodGenWorker implements IWorker private void generateUsingFeatures() { List chunkList = new LinkedList<>(); - ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY); + ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY); chunkList.add(chunk); LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk); @@ -565,7 +565,7 @@ public class LodGenWorker implements IWorker */ private void generateWithServer() { - lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.SERVER)); + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.SERVER)); } diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java index 736aea480..b4faaca6d 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java @@ -35,9 +35,9 @@ import com.seibel.lod.util.DetailDistanceUtil; import com.seibel.lod.util.LevelPosUtil; import com.seibel.lod.util.LodThreadFactory; import com.seibel.lod.util.LodUtil; +import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; import com.seibel.lod.wrappers.MinecraftWrapper; -import net.minecraft.util.math.ChunkPos; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.WorldWorkerManager; @@ -72,7 +72,7 @@ public class LodWorldGenerator */ public final AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0); - public final Set positionsWaitingToBeGenerated = new HashSet<>(); + public final Set positionsWaitingToBeGenerated = new HashSet<>(); /** * Singleton copy of this object @@ -142,7 +142,7 @@ public class LodWorldGenerator posZ = posToGenerate.getNthPosZ(nearIndex, true); nearIndex++; - ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ)); + ChunkPosWrapper chunkPos = new ChunkPosWrapper(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ)); // prevent generating the same chunk multiple times if (positionsWaitingToBeGenerated.contains(chunkPos)) @@ -167,7 +167,7 @@ public class LodWorldGenerator posZ = posToGenerate.getNthPosZ(farIndex, false); farIndex++; - ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ)); + ChunkPosWrapper chunkPos = new ChunkPosWrapper(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ)); // don't add more to the generation queue then allowed if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests) diff --git a/src/main/java/com/seibel/lod/handlers/ChunkLoader.java b/src/main/java/com/seibel/lod/handlers/ChunkLoader.java index 3c1b858ac..392e34a7a 100644 --- a/src/main/java/com/seibel/lod/handlers/ChunkLoader.java +++ b/src/main/java/com/seibel/lod/handlers/ChunkLoader.java @@ -24,7 +24,7 @@ import java.io.File; import com.seibel.lod.util.LodUtil; import com.seibel.lod.wrappers.MinecraftWrapper; -import net.minecraft.client.world.ClientWorld; +import com.seibel.lod.wrappers.World.LevelWrapper; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.chunk.IChunk; import net.minecraft.world.chunk.storage.ChunkSerializer; @@ -39,10 +39,10 @@ public class ChunkLoader { public static IChunk getChunkFromFile(ChunkPos pos){ - ClientWorld clientWorld = MinecraftWrapper.INSTANCE.getClientWorld(); - if (clientWorld == null) + LevelWrapper clientLevel = MinecraftWrapper.INSTANCE.getWrappedClientLevel(); + if (clientLevel == null) return null; - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientWorld.dimensionType()); + ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientLevel.getWorld().dimensionType()); try { File file = new File(serverWorld.getChunkSource().getDataStorage().dataFolder.getParent() + File.separatorChar + "region", "r." + (pos.x >> 5) + "." + (pos.z >> 5) + ".mca"); diff --git a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java index 33a172570..1c923ff5b 100644 --- a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java +++ b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java @@ -19,11 +19,9 @@ package com.seibel.lod.handlers; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.nio.file.Files; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -236,9 +234,7 @@ public class LodDimensionFileHandler // Save to File // //==============// - /** - * Save all dirty regions in this LodDimension to file. - */ + /** Save all dirty regions in this LodDimension to file */ public void saveDirtyRegionsToFileAsync() { fileWritingThreadPool.execute(saveDirtyRegionsThread); @@ -289,6 +285,7 @@ public class LodDimensionFileHandler } File oldFile = new File(fileName); //ClientProxy.LOGGER.info("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file."); + byte[] temp = region.getLevel(detailLevel).toDataString(); try { @@ -308,9 +305,12 @@ public class LodDimensionFileHandler // (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) @@ -326,11 +326,17 @@ public class LodDimensionFileHandler // delete anything the user may want. return; } - + if ((temp[1] & 0b10000000) != 0b10000000 && isFull == 0b10000000) + { + // existing file is complete while new one is only partially generate + // this can happen is for some reason loading failed + // this doesn't fix the bug, but at least protects old data + ClientProxy.LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + fileName + "]"); + return; + } // if we got this far then we are good // to overwrite the old file } - // the old file is good, now create a new temporary save file File newFile = new File(fileName + TMP_FILE_EXTENSION); try (XZCompressorOutputStream outputStream = new XZCompressorOutputStream(new FileOutputStream(newFile), 3)) @@ -339,7 +345,7 @@ public class LodDimensionFileHandler outputStream.write(LOD_SAVE_FILE_VERSION); // add each LodChunk to the file - outputStream.write(region.getLevel(detailLevel).toDataString()); + outputStream.write(temp); outputStream.close(); // overwrite the old file with the new one @@ -366,6 +372,22 @@ public class LodDimensionFileHandler // helper methods // //================// + public byte[] getHashFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality) + { + int regionX = regionPos.x; + int regionZ = regionPos.z; + String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality); + try (InputStream is = Files.newInputStream(Paths.get(fileName))) { + return org.apache.commons.codec.digest.DigestUtils.md5(is); + } + catch (IOException ioEx) + { + ClientProxy.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: "); + ioEx.printStackTrace(); + } + return new byte[0]; + } + /** * Return the name of the file that should contain the diff --git a/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java b/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java index 1eb0a5bdb..9ed3373c5 100644 --- a/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java +++ b/src/main/java/com/seibel/lod/handlers/ReflectionHandler.java @@ -37,7 +37,6 @@ import java.lang.reflect.Method; public class ReflectionHandler { public static final ReflectionHandler INSTANCE = new ReflectionHandler(); - private final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; public Field ofFogField = null; public Method vertexBufferUploadMethod = null; @@ -56,7 +55,7 @@ public class ReflectionHandler private void setupFogField() { // get every variable from the entity renderer - Field[] optionFields = mc.getOptions().getClass().getDeclaredFields(); + Field[] optionFields = MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields(); // try and find the ofFogType variable in gameSettings for (Field field : optionFields) @@ -93,7 +92,7 @@ public class ReflectionHandler try { - returnNum = (int) ofFogField.get(mc.getOptions()); + returnNum = (int) ofFogField.get(MinecraftWrapper.INSTANCE.getOptions()); } catch (IllegalArgumentException | IllegalAccessException e) { diff --git a/src/main/java/com/seibel/lod/objects/LodWorld.java b/src/main/java/com/seibel/lod/objects/LodWorld.java index 95d4ec998..b549aebb7 100644 --- a/src/main/java/com/seibel/lod/objects/LodWorld.java +++ b/src/main/java/com/seibel/lod/objects/LodWorld.java @@ -24,6 +24,7 @@ import java.util.Map; import com.seibel.lod.proxy.ClientProxy; +import com.seibel.lod.wrappers.World.DimensionTypeWrapper; import net.minecraft.world.DimensionType; /** @@ -39,7 +40,7 @@ public class LodWorld private String worldName; /** dimensions in this world */ - private Map lodDimensions; + private Map lodDimensions; /** If true then the LOD world is setup and ready to use */ private boolean isWorldLoaded = false; @@ -107,7 +108,7 @@ public class LodWorld if (lodDimensions == null) return; - lodDimensions.put(newDimension.dimension, newDimension); + lodDimensions.put(DimensionTypeWrapper.getDimensionTypeWrapper(newDimension.dimension), newDimension); } /** @@ -118,7 +119,7 @@ public class LodWorld if (lodDimensions == null) return null; - return lodDimensions.get(dimension); + return lodDimensions.get(DimensionTypeWrapper.getDimensionTypeWrapper(dimension)); } /** @@ -132,7 +133,7 @@ public class LodWorld saveAllDimensions(); - for (DimensionType key : lodDimensions.keySet()) + for (DimensionTypeWrapper key : lodDimensions.keySet()) lodDimensions.get(key).setRegionWidth(newRegionWidth); } @@ -148,7 +149,7 @@ public class LodWorld // but that requires a LodDimension.hasDirtyRegions() method or something similar ClientProxy.LOGGER.info("Saving LODs"); - for (DimensionType key : lodDimensions.keySet()) + for (DimensionTypeWrapper key : lodDimensions.keySet()) lodDimensions.get(key).saveDirtyRegionsToFileAsync(); } diff --git a/src/main/java/com/seibel/lod/objects/RegionPos.java b/src/main/java/com/seibel/lod/objects/RegionPos.java index 22657c571..20ae78de5 100644 --- a/src/main/java/com/seibel/lod/objects/RegionPos.java +++ b/src/main/java/com/seibel/lod/objects/RegionPos.java @@ -20,9 +20,8 @@ package com.seibel.lod.objects; import com.seibel.lod.util.LodUtil; - -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; +import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; /** * This object is similar to ChunkPos or BlockPos. @@ -55,28 +54,28 @@ public class RegionPos } /** Converts from a BlockPos to a RegionPos */ - public RegionPos(BlockPos pos) + public RegionPos(BlockPosWrapper pos) { - this(new ChunkPos(pos)); + this(new ChunkPosWrapper(pos)); } /** Converts from a ChunkPos to a RegionPos */ - public RegionPos(ChunkPos pos) + public RegionPos(ChunkPosWrapper pos) { - x = Math.floorDiv(pos.x, LodUtil.REGION_WIDTH_IN_CHUNKS); - z = Math.floorDiv(pos.z, LodUtil.REGION_WIDTH_IN_CHUNKS); + 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 ChunkPos chunkPos() + public ChunkPosWrapper chunkPos() { - return new ChunkPos( + return new ChunkPosWrapper( (x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2, (z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2); } /** Returns the BlockPos at the center of this region */ - public BlockPos blockPos() + public BlockPosWrapper blockPos() { return chunkPos().getWorldPosition() .offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2); diff --git a/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java b/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java index f692baa1d..01cb97756 100644 --- a/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java +++ b/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java @@ -129,7 +129,7 @@ public class VerticalLevelContainer implements LevelContainer long newData; detailLevel = inputData[index]; index++; - maxVerticalData = inputData[index]; + maxVerticalData = inputData[index] & 0b01111111; index++; size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); int x = size * size * maxVerticalData; @@ -181,24 +181,31 @@ public class VerticalLevelContainer implements LevelContainer public byte[] toDataString() { int index = 0; - int x = size * size * maxVerticalData; + 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++) { - current = dataContainer[i]; - for (tempIndex = 0; tempIndex < 8; tempIndex++) - tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex)); - index += 8; + 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; } diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index e99e09f64..bbf965f59 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -19,10 +19,10 @@ package com.seibel.lod.proxy; -import com.seibel.lod.wrappers.Chunk.ChunkWrapper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.GL15; import com.mojang.blaze3d.matrix.MatrixStack; import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder; @@ -40,8 +40,10 @@ import com.seibel.lod.util.DetailDistanceUtil; import com.seibel.lod.util.LodUtil; import com.seibel.lod.util.ThreadMapUtil; import com.seibel.lod.wrappers.MinecraftWrapper; +import com.seibel.lod.wrappers.Chunk.ChunkWrapper; import net.minecraft.profiler.IProfiler; +import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.text.StringTextComponent; import net.minecraftforge.client.event.InputEvent; import net.minecraftforge.event.TickEvent; @@ -54,7 +56,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; * This handles all events sent to the client, * and is the starting point for most of the mod. * @author James_Seibel - * @version 10-23-2021 + * @version 11-8-2021 */ public class ClientProxy { @@ -99,7 +101,7 @@ public class ClientProxy //==============// /** Do any setup that is required to draw LODs and then tell the LodRenderer to draw. */ - public void renderLods(MatrixStack mcMatrixStack, float partialTicks) + public void renderLods(MatrixStack mcModelViewMatrix, float partialTicks) { // comment out when creating a release // applyConfigOverrides(); @@ -129,6 +131,15 @@ public class ClientProxy lodDim.expandOrLoadRegionsAsync((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ()); + // get the default projection matrix, so we can + // reset it after drawing the LODs + float[] mcProjMatrixRaw = new float[16]; + GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw); + Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw); + // OpenGl outputs their matrices in col,row form instead of row,col + // (or maybe vice versa I have no idea :P) + mcProjectionMatrix.transpose(); + // Note to self: // if "unspecified" shows up in the pie chart, it is // possibly because the amount of time between sections @@ -137,7 +148,8 @@ public class ClientProxy profiler.pop(); // get out of "terrain" profiler.push("LOD"); - renderer.drawLODs(lodDim, mcMatrixStack, partialTicks, mc.getProfiler()); + + renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, mc.getProfiler()); profiler.pop(); // end LOD profiler.push("terrain"); // go back into "terrain" @@ -161,18 +173,19 @@ public class ClientProxy // remind the developer(s) that the config override is active if (!configOverrideReminderPrinted) { + // TODO add a send message method to the MC wrapper // mc.getPlayer().sendMessage(new StringTextComponent("LOD experimental build 1.5.1"), mc.getPlayer().getUUID()); // mc.getPlayer().sendMessage(new StringTextComponent("Here be dragons!"), mc.getPlayer().getUUID()); mc.getPlayer().sendMessage(new StringTextComponent("Debug settings enabled!"), mc.getPlayer().getUUID()); configOverrideReminderPrinted = true; } - + // LodConfig.CLIENT.graphics.drawResolution.set(HorizontalResolution.BLOCK); // LodConfig.CLIENT.worldGenerator.generationResolution.set(HorizontalResolution.BLOCK); // requires a world restart? // LodConfig.CLIENT.worldGenerator.lodQualityMode.set(VerticalQuality.VOXEL); - + // LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.set(FogDistance.FAR); // LodConfig.CLIENT.graphics.fogQualityOption.fogDrawOverride.set(FogDrawOverride.FANCY); // LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.set(true); @@ -181,7 +194,7 @@ public class ClientProxy // LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.set(VanillaOverdraw.DYNAMIC); // LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.BUFFER_STORAGE); - + // LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); // LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(128); // LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); @@ -250,23 +263,25 @@ public class ClientProxy { // 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 - //LodNodeGenWorker.restartExecutorService(); + //LodNodeGenWorker.restartExecutorService(); // TODO why was this commented out? -James //ThreadMapUtil.clearMaps(); LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0); lodWorld.deselectWorld(); - // hopefully this should reduce issues related to the buffer builder + // prevent issues related to the buffer builder // breaking when changing worlds. renderer.destroyBuffers(); recalculateWidths = true; renderer = new LodRenderer(lodBufferBuilder); - // make sure the nilled objects are freed. + // make sure the nulled objects are freed. // (this prevents an out of memory error when // changing worlds) System.gc(); @@ -314,7 +329,7 @@ public class ClientProxy private void playerMoveEvent(LodDimension lodDim) { // make sure the dimension is centered - RegionPos playerRegionPos = new RegionPos(mc.getPlayer().blockPosition()); + 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) { @@ -330,7 +345,7 @@ public class ClientProxy { // calculate how wide the dimension(s) should be in regions int chunksWide; - if (mc.getClientWorld().dimensionType().hasCeiling()) + if (mc.getClientLevel().dimensionType().hasCeiling()) chunksWide = Math.min(LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1; else chunksWide = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * 2 + 1; @@ -347,7 +362,7 @@ public class ClientProxy // update the dimensions to fit the new width lodWorld.resizeDimensionRegionWidth(newWidth); lodBuilder.defaultDimensionWidthInRegions = newWidth; - renderer.setupBuffers(lodWorld.getLodDimension(mc.getClientWorld().dimensionType())); + renderer.setupBuffers(lodWorld.getLodDimension(mc.getClientLevel().dimensionType())); recalculateWidths = false; //LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth ); @@ -368,6 +383,7 @@ public class ClientProxy /** this method reset some static data every time we change world */ private void resetMod() { + // TODO when should this be used? ThreadMapUtil.clearMaps(); LodGenWorker.restartExecutorService(); diff --git a/src/main/java/com/seibel/lod/proxy/GlProxy.java b/src/main/java/com/seibel/lod/proxy/GlProxy.java index cfecd809c..90df6f79d 100644 --- a/src/main/java/com/seibel/lod/proxy/GlProxy.java +++ b/src/main/java/com/seibel/lod/proxy/GlProxy.java @@ -21,11 +21,16 @@ package com.seibel.lod.proxy; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GLCapabilities; import com.mojang.blaze3d.systems.RenderSystem; import com.seibel.lod.ModInfo; import com.seibel.lod.enums.GlProxyContext; +import com.seibel.lod.render.shader.LodShader; +import com.seibel.lod.render.shader.LodShaderProgram; import com.seibel.lod.wrappers.MinecraftWrapper; /** @@ -40,29 +45,31 @@ import com.seibel.lod.wrappers.MinecraftWrapper; * https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one

* * @author James Seibel - * @version 10-23-2021 + * @version 11-8-2021 */ public class GlProxy { private static GlProxy instance = null; - private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE; + private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE; + /** Minecraft's GLFW window */ public final long minecraftGlContext; - /** Minecraft's GL context */ + /** Minecraft's GL capabilities */ public final GLCapabilities minecraftGlCapabilities; /** the LodBuilder's GLFW window */ public final long lodBuilderGlContext; - /** the LodBuilder's GL context */ + /** the LodBuilder's GL capabilities */ public final GLCapabilities lodBuilderGlCapabilities; - /** - * This is just used for debugging, hopefully it can be removed once - * the context switching is more stable. - */ - public Thread lodBuilderOwnerThread = null; + + /** 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; + /** Does this computer's GPU support fancy fog? */ public final boolean fancyFogAvailable; @@ -74,6 +81,8 @@ public class GlProxy public final boolean mapBufferRangeSupported; + + private GlProxy() { ClientProxy.LOGGER.error("Creating " + GlProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error."); @@ -93,54 +102,58 @@ public class GlProxy minecraftGlContext = GLFW.glfwGetCurrentContext(); minecraftGlCapabilities = GL.getCapabilities(); - // create the LodBuilder's context - // Hopefully this shouldn't cause any issues with other mods that need custom contexts - // (although the number that do should be relatively few) + // 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 an invisible window to hold the context - lodBuilderGlContext = GLFW.glfwCreateWindow(640, 480, "LOD window", 0L, minecraftGlContext); + + // create the LodBuilder context + lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, minecraftGlContext); GLFW.glfwMakeContextCurrent(lodBuilderGlContext); lodBuilderGlCapabilities = GL.createCapabilities(); - // Since this is called on the render thread, make sure the Minecraft context is used in the end - GLFW.glfwMakeContextCurrent(minecraftGlContext); - GL.setCapabilities(minecraftGlCapabilities); - //==============================// - // determine the OpenGL version // - //==============================// - - bufferStorageSupported = minecraftGlCapabilities.OpenGL45; - mapBufferRangeSupported = minecraftGlCapabilities.OpenGL30; - - if (!minecraftGlCapabilities.OpenGL15) - { - String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 1.5 or greater."; - mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 1.5 or greater.")); - } - - if (!bufferStorageSupported) - { - String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5"; - - ClientProxy.LOGGER.error("This GPU doesn't support OpenGL 4.5, falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance."); - } - - - //==================================// // get any GPU related capabilities // //==================================// - // see if this GPU can run fancy fog - fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance; + ClientProxy.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 + // TODO re-add buffer storage support + bufferStorageSupported = false; //lodBuilderGlCapabilities.glBufferStorage != 0; + mapBufferRangeSupported = lodBuilderGlCapabilities.glMapBufferRange != 0; + fancyFogAvailable = minecraftGlCapabilities.GL_NV_fog_distance; + + + // display the capabilities + if (!bufferStorageSupported) + { + String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5"; + ClientProxy.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 (!fancyFogAvailable) ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that the fancy fog option will not be available."); @@ -148,11 +161,79 @@ public class GlProxy + + //==============// + // shader setup // + //==============// + + //setGlContext(GlProxyContext.LOD_RENDER); + setGlContext(GlProxyContext.MINECRAFT); + + createShaderProgram(); + + + // Note: VAO objects can not be shared between contexts, + // this must be created on the LOD 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 ClientProxy.LOGGER.error(GlProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day."); } - + /** Creates all required shaders */ + public void createShaderProgram() + { + LodShader vertexShader = null; + LodShader fragmentShader = null; + + try + { + // get the shaders from the resource folder + vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/unshaded.vert", false); + fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/unshaded.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/unshaded.vert", true); +// fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/unshaded.frag", true); + + + // create the shaders + + lodShaderProgram = new LodShaderProgram(); + + // Attach the compiled shaders to the program + 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); + } + catch (Exception e) + { + ClientProxy.LOGGER.error("Unable to compile shaders. Error: " + e.getMessage()); + } + } /** @@ -163,12 +244,12 @@ public class GlProxy { GlProxyContext currentContext = getGlContext(); - // we don't have to change the context, we're already there. + // we don't have to change the context, we are already there. if (currentContext == newContext) return; - long contextPointer = 0L; + long contextPointer; GLCapabilities newGlCapabilities = null; // get the pointer(s) for this context @@ -187,20 +268,13 @@ public class GlProxy 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); - - - - // used for debugging - if (newContext == GlProxyContext.LOD_BUILDER) - lodBuilderOwnerThread = Thread.currentThread(); - else if (newContext == GlProxyContext.NONE && currentContext == GlProxyContext.LOD_BUILDER) - lodBuilderOwnerThread = null; } @@ -220,9 +294,12 @@ public class GlProxy else if (currentContext == 0L) return GlProxyContext.NONE; else - // hopefully this shouldn't happen, but - // at least now we will be notified if it does happen - throw new IllegalStateException(Thread.currentThread().getName() + " has a unknown OpenGl context: [" + currentContext + "]. Minecraft context [" + minecraftGlContext + "], LodBuilder context [" + lodBuilderGlContext + "], no context [0]."); + // hopefully this shouldn't happen + throw new IllegalStateException(Thread.currentThread().getName() + + " has a unknown OpenGl context: [" + currentContext + "]. " + + "Minecraft context [" + minecraftGlContext + "], " + + "LodBuilder context [" + lodBuilderGlContext + "], " + + "no context [0]."); } diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java index fd8608a23..6e23ae809 100644 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodRenderer.java @@ -19,44 +19,52 @@ package com.seibel.lod.render; +import java.util.HashSet; + +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.NVFogDistance; + import com.mojang.blaze3d.matrix.MatrixStack; -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder; import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder.VertexBuffersAndOffset; import com.seibel.lod.config.LodConfig; -import com.seibel.lod.enums.*; +import com.seibel.lod.enums.DebugMode; +import com.seibel.lod.enums.FogDistance; +import com.seibel.lod.enums.FogDrawOverride; +import com.seibel.lod.enums.FogQuality; +import com.seibel.lod.enums.GpuUploadMethod; import com.seibel.lod.handlers.ReflectionHandler; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.NearFarFogSettings; import com.seibel.lod.objects.RegionPos; import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.proxy.GlProxy; +import com.seibel.lod.render.shader.LodShaderProgram; import com.seibel.lod.util.DetailDistanceUtil; import com.seibel.lod.util.LevelPosUtil; import com.seibel.lod.util.LodUtil; import com.seibel.lod.wrappers.MinecraftWrapper; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; +import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; + import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.vertex.VertexBuffer; import net.minecraft.potion.Effects; import net.minecraft.profiler.IProfiler; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Vector3d; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL15C; -import org.lwjgl.opengl.NVFogDistance; - -import java.util.HashSet; - +import net.minecraft.util.math.vector.Vector3f; /** * This is where all the magic happens.
* This is where LODs are draw to the world. + * * @author James Seibel - * @version 10-25-2021 + * @version 11-8-2021 */ public class LodRenderer { @@ -90,7 +98,7 @@ public class LodRenderer */ private int[][][] storageBufferIds; - private ChunkPos vbosCenter = new ChunkPos(0, 0); + private ChunkPosWrapper vbosCenter = new ChunkPosWrapper(0, 0); /** This is used to determine if the LODs should be regenerated */ @@ -124,8 +132,6 @@ public class LodRenderer public boolean vanillaRenderedChunksEmptySkip = false; public int vanillaBlockRenderedDistance; - final boolean vivecraftDetected = ReflectionHandler.INSTANCE.detectVivecraft(); - @@ -147,11 +153,11 @@ public class LodRenderer * Besides drawing the LODs this method also starts * the async process of generating the Buffers that hold those LODs. * @param lodDim The dimension to draw, if null doesn't replace the current dimension. - * @param mcMatrixStack This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method + * @param 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. */ - @SuppressWarnings("deprecation") - public void drawLODs(LodDimension lodDim, MatrixStack mcMatrixStack, float partialTicks, IProfiler newProfiler) + public void drawLODs(LodDimension lodDim, MatrixStack mcModelViewMatrix, Matrix4f mcProjectionMatrix, float partialTicks, IProfiler newProfiler) { //=================================// // determine if LODs should render // @@ -176,14 +182,6 @@ public class LodRenderer - //===============// - // initial setup // - //===============// - - profiler = newProfiler; - profiler.push("LOD setup"); - - // TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead) // starting here... determineIfLodsShouldRegenerate(lodDim, partialTicks); @@ -197,25 +195,17 @@ public class LodRenderer // 2. we aren't already regenerating the LODs // 3. we aren't waiting for the build and draw buffers to swap // (this is to prevent thread conflicts) - if (LodConfig.CLIENT.advancedModOptions.debugging.drawLods.get()) + if ((partialRegen || fullRegen) && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvailable()) { - if (lodBufferBuilder.buildableBuffers == null) - lodBufferBuilder.setupBuffers(lodDim); + // generate the LODs on a separate thread to prevent stuttering or freezing + lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayerBlockPos(), true); - if ((partialRegen || fullRegen) && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvailable()) - { - // generate the LODs on a separate thread to prevent stuttering or freezing - lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayer().blockPosition(), fullRegen); - - // the regen process has been started, - // it will be done when lodBufferBuilder.newBuffersAvailable() - // is true - fullRegen = false; - partialRegen = false; - } + // the regen process has been started, + // it will be done when lodBufferBuilder.newBuffersAvailable() + // is true + fullRegen = false; + partialRegen = false; } - else - lodBufferBuilder.destroyBuffers(); // TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead) // ...ending here @@ -226,9 +216,19 @@ public class LodRenderer } - //===========================// - // GL settings for rendering // - //===========================// + + + + //===============// + // initial setup // + //===============// + + profiler = newProfiler; + profiler.push("LOD setup"); + + GlProxy glProxy = GlProxy.getInstance(); + + // set the required open GL settings @@ -237,49 +237,47 @@ public class LodRenderer else GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL); - GL15.glDisable(GL15.GL_TEXTURE_2D); GL15.glEnable(GL15.GL_CULL_FACE); - GL15.glEnable(GL15.GL_COLOR_MATERIAL); GL15.glEnable(GL15.GL_DEPTH_TEST); // enable transparent rendering GL15.glBlendFunc(GL15.GL_SRC_ALPHA, GL15.GL_ONE_MINUS_SRC_ALPHA); GL15.glEnable(GL15.GL_BLEND); - // disable the lights Minecraft uses - GL15.glDisable(GL15.GL_LIGHT0); - GL15.glDisable(GL15.GL_LIGHT1); + // get MC's shader program + int currentProgram = GL20.glGetInteger(GL20.GL_CURRENT_PROGRAM); - // get the default projection matrix, so we can - // reset it after drawing the LODs - float[] mcProjMatrixRaw = new float[16]; - GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw); - Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw); - // OpenGl outputs their matrices in col,row form instead of row,col - // (or maybe vice versa I have no idea :P) - mcProjectionMatrix.transpose(); - Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcMatrixStack, partialTicks); + Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcModelViewMatrix, partialTicks); vanillaBlockRenderedDistance = mc.getRenderDistance() * LodUtil.CHUNK_WIDTH; // required for setupFog and setupProjectionMatrix - if (mc.getClientWorld().dimensionType().hasCeiling()) + if (mc.getClientLevel().dimensionType().hasCeiling()) farPlaneBlockDistance = Math.min(LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH; else farPlaneBlockDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH; - setupProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks); + + Matrix4f projectionMatrix = createProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks); + // commented out until we can add shaders to handle lighting //setupLighting(lodDim, partialTicks); - NearFarFogSettings fogSettings = determineFogSettings(); - // determine the current fog settings, so they can be - // reset after drawing the LODs - float defaultFogStartDist = GL15.glGetFloat(GL15.GL_FOG_START); - float defaultFogEndDist = GL15.glGetFloat(GL15.GL_FOG_END); - int defaultFogMode = GL15.glGetInteger(GL15.GL_FOG_MODE); - int defaultFogDistance = GlProxy.getInstance().fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1; +// // determine the current fog settings, so they can be +// // reset after drawing the LODs +// float defaultFogStartDist = GL15.glGetFloat(GL15.GL_FOG_START); +// float defaultFogEndDist = GL15.glGetFloat(GL15.GL_FOG_END); +// int defaultFogMode = GL15.glGetInteger(GL15.GL_FOG_MODE); +// int defaultFogDistance = glProxy.fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1; + + //ShaderInstance mcShader = RenderSystem.getShader(); + +// NearFarFogSettings fogSettings = determineFogSettings(); + + + + //===========// // rendering // @@ -287,13 +285,14 @@ public class LodRenderer profiler.popPush("LOD draw"); - if (vbos != null && LodConfig.CLIENT.advancedModOptions.debugging.drawLods.get()) + if (vbos != null) { - ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera(); - Vector3d cameraDir = new Vector3d(renderInfo.getLookVector()); + ActiveRenderInfo camera = mc.getGameRenderer().getMainCamera(); + Vector3f cameraDir = camera.getLookVector(); - boolean cullingDisabled = LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get(); - boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && GlProxy.getInstance().bufferStorageSupported; + // TODO re-enable once rendering is totally working + boolean cullingDisabled = true; //LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get(); + boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported; // used to determine what type of fog to render int halfWidth = vbos.length / 2; @@ -303,6 +302,32 @@ public class LodRenderer RegionPos vboCenterRegionPos = new RegionPos(vbosCenter); + + + // 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); + + + + // upload the required uniforms + int mvmUniform = shaderProgram.getUniformLocation("modelViewMatrix"); + shaderProgram.setUniform(mvmUniform, modelViewMatrix); + int projUniform = shaderProgram.getUniformLocation("projectionMatrix"); + shaderProgram.setUniform(projUniform, projectionMatrix); + + + // render each of the buffers for (int x = 0; x < vbos.length; x++) { for (int z = 0; z < vbos.length; z++) @@ -311,27 +336,34 @@ public class LodRenderer x + vboCenterRegionPos.x - (lodDim.getWidth() / 2), z + vboCenterRegionPos.z - (lodDim.getWidth() / 2)); - if (cullingDisabled || RenderUtil.isRegionInViewFrustum(renderInfo.getBlockPosition(), cameraDir, vboPos.blockPos())) + if (cullingDisabled || RenderUtil.isRegionInViewFrustum(camera.getBlockPosition(), cameraDir, vboPos.blockPos())) { - if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth) - && (z > halfWidth - quarterWidth && z < halfWidth + quarterWidth)) - setupFog(fogSettings.near.distance, fogSettings.near.quality); - else - setupFog(fogSettings.far.distance, fogSettings.far.quality); + // TODO add fog to the fragment shader +// if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth) +// && (z > halfWidth - quarterWidth && z < halfWidth + quarterWidth)) +// setupFog(fogSettings.near.distance, fogSettings.near.quality); +// else +// setupFog(fogSettings.far.distance, fogSettings.far.quality); - - if (storageBufferIds != null && renderBufferStorage) - for (int i = 0; i < storageBufferIds[x][z].length; i++) - drawStorageBuffer(vbos[x][z][i], storageBufferIds[x][z][i], modelViewMatrix); - else +// if (storageBufferIds != null && renderBufferStorage) +// for (int i = 0; i < storageBufferIds[x][z].length; i++) +// drawArrays(storageBufferIds[x][z][i], vbos[x][z][i].vertexCount, posAttrib, colAttrib); +// else for (int i = 0; i < vbos[x][z].length; i++) - drawVertexBuffer(vbos[x][z][i], modelViewMatrix); + drawArrays(vbos[x][z][i].id, vbos[x][z][i].vertexCount, posAttrib, colAttrib); } } } + + + GL20.glDisableVertexAttribArray(posAttrib); + GL20.glDisableVertexAttribArray(colAttrib); } + + + //=========// // cleanup // //=========// @@ -339,23 +371,12 @@ public class LodRenderer profiler.popPush("LOD cleanup"); GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL); - GL15.glEnable(GL15.GL_TEXTURE_2D); - GL15.glDisable(LOD_GL_LIGHT_NUMBER); - GL15.glDisable(GL15.GL_BLEND); - // re-enable the lights Minecraft uses - GL15.glEnable(GL15.GL_LIGHT0); - GL15.glEnable(GL15.GL_LIGHT1); - RenderSystem.disableLighting(); + GL15.glDisable(GL15.GL_BLEND); // TODO: what should this be reset to? - // reset the fog settings so the normal chunks - // will be drawn correctly - cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance); + GL20.glUseProgram(currentProgram); + //RenderSystem.setShader(() -> mcShader); - // reset the projection matrix so anything drawn after - // the LODs will use the correct projection matrix - gameRender.resetProjectionMatrix(mcProjectionMatrix); - - // clear the depth buffer so anything drawn is drawn + // clear the depth buffer so everything drawn is drawn // over the LODs GL15.glClear(GL15.GL_DEPTH_BUFFER_BIT); @@ -364,40 +385,40 @@ public class LodRenderer profiler.pop(); } - /** This is where the actual drawing happens. */ - private void drawStorageBuffer(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix) + private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib) { - if (vbo == null) + if (glBufferId == 0) return; - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId); - // 0L is the starting pointer - LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L); + // can be used to check for OpenGL errors +// int error = GL15.glGetError(); +// ClientProxy.LOGGER.info(Integer.toHexString(error)); - vbo.draw(modelViewMatrix, GL15.GL_QUADS); - GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - LodUtil.LOD_VERTEX_FORMAT.clearBufferState(); + // 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); + + + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + GL30.glBindVertexArray(0); + + GL20.glDisableVertexAttribArray(posAttrib); + GL20.glDisableVertexAttribArray(colAttrib); } - /** This is where the actual drawing happens. */ - private void drawVertexBuffer(VertexBuffer vbo, Matrix4f modelViewMatrix) - { - if (vbo == null) - return; - - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); - // 0L is the starting pointer - LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L); - - vbo.draw(modelViewMatrix, GL15.GL_QUADS); - - GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - LodUtil.LOD_VERTEX_FORMAT.clearBufferState(); - } - @@ -405,7 +426,6 @@ public class LodRenderer // Setup Functions // //=================// - @SuppressWarnings("deprecation") private void setupFog(FogDistance fogDistance, FogQuality fogQuality) { if (fogQuality == FogQuality.OFF) @@ -445,55 +465,52 @@ public class LodRenderer { // for more realistic fog when using FAR if (LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get() == FogDistance.NEAR_AND_FAR) - RenderSystem.fogStart(farPlaneBlockDistance * 1.6f * 0.9f); + GL15.glFogf(GL15.GL_FOG_START, farPlaneBlockDistance * 1.6f * 0.9f); else - RenderSystem.fogStart(Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f)); - RenderSystem.fogEnd(farPlaneBlockDistance * 1.6f); + GL15.glFogf(GL15.GL_FOG_START, Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f)); + GL15.glFogf(GL15.GL_FOG_END, farPlaneBlockDistance * 1.6f); } else if (fogQuality == FogQuality.FAST) { // for the far fog of the normal chunks // to start right where the LODs' end use: // end = 0.8f, start = 1.5f - RenderSystem.fogStart(farPlaneBlockDistance * 0.75f); - RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f); + GL15.glFogf(GL15.GL_FOG_START, farPlaneBlockDistance * 0.75f); + GL15.glFogf(GL15.GL_FOG_END, farPlaneBlockDistance * 1.0f); } } else if (fogDistance == FogDistance.NEAR) { if (fogQuality == FogQuality.FANCY) { - RenderSystem.fogEnd(vanillaBlockRenderedDistance * 1.41f); - RenderSystem.fogStart(vanillaBlockRenderedDistance * 1.6f); + GL15.glFogf(GL15.GL_FOG_END, vanillaBlockRenderedDistance * 1.41f); + GL15.glFogf(GL15.GL_FOG_START, vanillaBlockRenderedDistance * 1.6f); } else if (fogQuality == FogQuality.FAST) { - RenderSystem.fogEnd(vanillaBlockRenderedDistance * 1.0f); - RenderSystem.fogStart(vanillaBlockRenderedDistance * 1.5f); + GL15.glFogf(GL15.GL_FOG_END, vanillaBlockRenderedDistance * 1.0f); + GL15.glFogf(GL15.GL_FOG_START, vanillaBlockRenderedDistance * 1.5f); } } GL15.glEnable(GL15.GL_FOG); - RenderSystem.enableFog(); - RenderSystem.setupNvFogDistance(); - RenderSystem.fogMode(GlStateManager.FogMode.LINEAR); + GL15.glFogi(GL15.GL_FOG_MODE, GL15.GL_LINEAR); if (GlProxy.getInstance().fancyFogAvailable) GL15.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode); } - /** + /** * Revert any changes that were made to the fog * and sets up the fog for Minecraft. */ - @SuppressWarnings("deprecation") private void cleanupFog(NearFarFogSettings fogSettings, float defaultFogStartDist, float defaultFogEndDist, int defaultFogMode, int defaultFogDistance) { - RenderSystem.fogStart(defaultFogStartDist); - RenderSystem.fogEnd(defaultFogEndDist); - RenderSystem.fogMode(defaultFogMode); + GL15.glFogf(GL15.GL_FOG_START, defaultFogStartDist); + GL15.glFogf(GL15.GL_FOG_END, defaultFogEndDist); + GL15.glFogi(GL15.GL_FOG_MODE, defaultFogMode); // this setting is only valid if the GPU supports fancy fog if (GlProxy.getInstance().fancyFogAvailable) @@ -502,7 +519,7 @@ public class LodRenderer // disable fog if Minecraft wasn't rendering fog // or we want it disabled if (!fogSettings.vanillaIsRenderingFog - || LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.get()) + || LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.get()) { // Make fog render a infinite distance away. // This doesn't technically disable Minecraft's fog @@ -512,9 +529,9 @@ public class LodRenderer // we can't disable minecraft's fog outright because by default // minecraft will re-enable the fog after our code - RenderSystem.fogStart(0.0F); - RenderSystem.fogEnd(Float.MAX_VALUE); - RenderSystem.fogDensity(0.0F); + GL15.glFogf(GL15.GL_FOG_START, 0.0F); + GL15.glFogf(GL15.GL_FOG_END, Float.MAX_VALUE); + GL15.glFogf(GL15.GL_FOG_DENSITY, Float.MAX_VALUE); } } @@ -526,87 +543,72 @@ public class LodRenderer * (since AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher * accuracy vs the model view matrix, which only uses floats) */ - private Matrix4f offsetTheModelViewMatrix(MatrixStack mcMatrixStack, float partialTicks) + private Matrix4f offsetTheModelViewMatrix(MatrixStack mcModelViewMatrix, float partialTicks) { // duplicate the last matrix - mcMatrixStack.pushPose(); + mcModelViewMatrix.pushPose(); // get all relevant camera info - ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera(); - Vector3d projectedView = renderInfo.getPosition(); + ActiveRenderInfo camera = mc.getGameRenderer().getMainCamera(); + Vector3d projectedView = camera.getPosition(); // translate the camera relative to the regions' center // (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher // accuracy vs the model view matrix, which only uses floats) - BlockPos bufferPos = vbosCenter.getWorldPosition(); + BlockPosWrapper bufferPos = vbosCenter.getWorldPosition(); double xDiff = projectedView.x - bufferPos.getX(); double zDiff = projectedView.z - bufferPos.getZ(); - mcMatrixStack.translate(-xDiff, -projectedView.y, -zDiff); + mcModelViewMatrix.translate(-xDiff, -projectedView.y, -zDiff); // get the modified model view matrix - Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose(); + Matrix4f lodModelViewMatrix = mcModelViewMatrix.last().pose(); // remove the lod ModelViewMatrix - mcMatrixStack.popPose(); + mcModelViewMatrix.popPose(); return lodModelViewMatrix; } - + /** - * create a new projection matrix and send it over to the GPU + * 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 * @param partialTicks how many ticks into the frame we are */ - private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks) + private Matrix4f createProjectionMatrix(Matrix4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks) { - Matrix4f lodPoj; - float nearClipPlane = LodConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get() ? vanillaBlockRenderedDistance / 5 : 1; - float farClipPlane = farPlaneBlockDistance * LodUtil.CHUNK_WIDTH >> 1; + // create the new projection matrix + Matrix4f lodProj = + Matrix4f.perspective( + getFov(partialTicks, true), + (float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(), + LodConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get() ? vanillaBlockRenderedDistance / 5 : 1, + farPlaneBlockDistance * LodUtil.CHUNK_WIDTH / 2); - if (vivecraftDetected) - { - //use modify clip plane method to modify the current projection matrix's clip planes. - lodPoj = ReflectionHandler.INSTANCE.Matrix4fModifyClipPlanes( - currentProjectionMatrix, - nearClipPlane, - farClipPlane); - } - else - { - // create the new projection matrix - lodPoj = Matrix4f.perspective( - getFov(partialTicks, true), - (float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(), - nearClipPlane, - farClipPlane); - - // get Minecraft's un-edited projection matrix - // (this is before it is zoomed, distorted, etc.) - Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, true); - // true here means use "use fov setting" (probably) - - - // this logic strips away the defaultMcProj matrix, so we - // can get the distortionMatrix, which represents all - // transformations, zooming, distortions, etc. done - // to Minecraft's Projection matrix - Matrix4f defaultMcProjInv = defaultMcProj.copy(); - defaultMcProjInv.invert(); - - Matrix4f distortionMatrix = defaultMcProjInv.copy(); - distortionMatrix.multiply(currentProjectionMatrix); - - - // edit the lod projection to match Minecraft's - // (so the LODs line up with the real world) - lodPoj.multiply(distortionMatrix); - } + // get Minecraft's un-edited projection matrix + // (this is before it is zoomed, distorted, etc.) + Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, true); + // true here means use "use fov setting" (probably) - // send the projection over to the GPU - gameRender.resetProjectionMatrix(lodPoj); + + // this logic strips away the defaultMcProj matrix, so we + // can get the distortionMatrix, which represents all + // transformations, zooming, distortions, etc. done + // to Minecraft's Projection matrix + Matrix4f defaultMcProjInv = defaultMcProj.copy(); + defaultMcProjInv.invert(); + + Matrix4f distortionMatrix = defaultMcProjInv.copy(); + distortionMatrix.multiply(currentProjectionMatrix); + + + // edit the lod projection to match Minecraft's + // (so the LODs line up with the real world) + lodProj.multiply(distortionMatrix); + + return lodProj; } @@ -695,7 +697,7 @@ public class LodRenderer lodBufferBuilder.destroyBuffers(); } - + // TODO move this into the MC wrapper private double getFov(float partialTicks, boolean useFovSetting) { return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting); @@ -804,8 +806,6 @@ public class LodRenderer /** Determines if the LODs should have a fullRegen or partialRegen */ private void determineIfLodsShouldRegenerate(LodDimension lodDim, float partialTicks) { - - short chunkRenderDistance = (short) mc.getRenderDistance(); int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2; @@ -815,14 +815,14 @@ public class LodRenderer // check if the view distance changed if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() - || chunkRenderDistance != prevRenderDistance - || prevFogDistance != LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get()) + || chunkRenderDistance != prevRenderDistance + || prevFogDistance != LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get()) { vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; DetailDistanceUtil.updateSettings(); fullRegen = true; - previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk); + previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayerChunkPos().getZ(), mc.getPlayerChunkPos().getZ()); prevFogDistance = LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get(); prevRenderDistance = chunkRenderDistance; } @@ -841,12 +841,12 @@ public class LodRenderer if (newTime - prevPlayerPosTime > LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get().playerMoveTimeout) { if (LevelPosUtil.getDetailLevel(previousPos) == 0 - || mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos) - || mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos)) + || mc.getPlayerChunkPos().getX() != LevelPosUtil.getPosX(previousPos) + || mc.getPlayerChunkPos().getZ() != LevelPosUtil.getPosZ(previousPos)) { vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; fullRegen = true; - previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk); + previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayerChunkPos().getX(), mc.getPlayerChunkPos().getZ()); } prevPlayerPosTime = newTime; } @@ -875,17 +875,23 @@ public class LodRenderer // check if the lighting changed if (Math.abs(skyBrightness - prevSkyBrightness) > minLightingDifference - // make sure the lighting gets to the max/minimum value - // (just in case the minLightingDifference is too large to notice the change) - || (skyBrightness == 1.0f && prevSkyBrightness != 1.0f) // noon - || (skyBrightness == 0.2f && prevSkyBrightness != 0.2f) // midnight - || mc.getOptions().gamma != prevBrightness) + // make sure the lighting gets to the max/minimum value + // (just in case the minLightingDifference is too large to notice the change) + || (skyBrightness == 1.0f && prevSkyBrightness != 1.0f) // noon + || (skyBrightness == 0.2f && prevSkyBrightness != 0.2f) // midnight + || mc.getOptions().gamma != prevBrightness) { fullRegen = true; prevBrightness = mc.getOptions().gamma; prevSkyBrightness = skyBrightness; } + /*if (lightMap != lastLightMap) + { + fullRegen = true; + lastLightMap = lightMap; + }*/ + //================// // partial regens // //================// @@ -921,21 +927,21 @@ public class LodRenderer //==============// // determine which LODs should not be rendered close to the player - HashSet chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayer().blockPosition()); + HashSet chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayerBlockPos()); int xIndex; int zIndex; for (ChunkPos pos : chunkPosToSkip) { vanillaRenderedChunksEmptySkip = false; - xIndex = (pos.x - mc.getPlayer().xChunk) + (chunkRenderDistance + 1); - zIndex = (pos.z - mc.getPlayer().zChunk) + (chunkRenderDistance + 1); + xIndex = (pos.x - mc.getPlayerChunkPos().getX()) + (chunkRenderDistance + 1); + zIndex = (pos.z - mc.getPlayerChunkPos().getZ()) + (chunkRenderDistance + 1); // sometimes we are given chunks that are outside the render distance, // This prevents index out of bounds exceptions if (xIndex >= 0 && zIndex >= 0 - && xIndex < vanillaRenderedChunks.length - && zIndex < vanillaRenderedChunks.length) + && xIndex < vanillaRenderedChunks.length + && zIndex < vanillaRenderedChunks.length) { if (!vanillaRenderedChunks[xIndex][zIndex]) { @@ -954,6 +960,8 @@ public class LodRenderer vanillaRenderedChunksChanged = true; vanillaRenderedChunksEmptySkip = true; } + + vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth]; } } diff --git a/src/main/java/com/seibel/lod/render/RenderUtil.java b/src/main/java/com/seibel/lod/render/RenderUtil.java index 8835f4cda..5397dcf04 100644 --- a/src/main/java/com/seibel/lod/render/RenderUtil.java +++ b/src/main/java/com/seibel/lod/render/RenderUtil.java @@ -21,10 +21,11 @@ package com.seibel.lod.render; import com.seibel.lod.util.LodUtil; import com.seibel.lod.wrappers.MinecraftWrapper; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; +import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.util.math.vector.Vector3f; /** * This holds miscellaneous helper code @@ -42,13 +43,13 @@ public class RenderUtil * 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(ChunkPos pos, ChunkPos center) + public static boolean isChunkPosInLoadedArea(ChunkPosWrapper pos, ChunkPosWrapper center) { - return (pos.x >= center.x - mc.getRenderDistance() - && pos.x <= center.x + mc.getRenderDistance()) + return (pos.getX() >= center.getX() - mc.getRenderDistance() + && pos.getX() <= center.getX() + mc.getRenderDistance()) && - (pos.z >= center.z - mc.getRenderDistance() - && pos.z <= center.z + mc.getRenderDistance()); + (pos.getZ() >= center.getZ() - mc.getRenderDistance() + && pos.getZ() <= center.getZ() + mc.getRenderDistance()); } /** @@ -85,22 +86,24 @@ public class RenderUtil * Returns true if one of the region's 4 corners is in front * of the camera. */ - public static boolean isRegionInViewFrustum(BlockPos playerBlockPos, Vector3d cameraDir, BlockPos vboCenterPos) + public static boolean isRegionInViewFrustum(BlockPos playerBlockPos, Vector3f cameraDir, BlockPosWrapper vboCenterPos) { // convert the vbo position into a direction vector // starting from the player's position - Vector3d vboVec = new Vector3d(vboCenterPos.getX(), 0, vboCenterPos.getZ()); - Vector3d playerVec = new Vector3d(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ()); - Vector3d vboCenterVec = vboVec.subtract(playerVec); + Vector3f vboVec = new Vector3f(vboCenterPos.getX(), 0, vboCenterPos.getZ()); + Vector3f playerVec = new Vector3f(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ()); + + vboVec.sub(playerVec); + Vector3f vboCenterVec = vboVec; int halfRegionWidth = LodUtil.REGION_WIDTH / 2; // calculate the 4 corners - Vector3d vboSeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth); - Vector3d vboSwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth); - Vector3d vboNwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth); - Vector3d vboNeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth); + Vector3f vboSeVec = new Vector3f(vboCenterVec.x() + halfRegionWidth, vboCenterVec.y(), vboCenterVec.z() + halfRegionWidth); + Vector3f vboSwVec = new Vector3f(vboCenterVec.x() - halfRegionWidth, vboCenterVec.y(), vboCenterVec.z() + halfRegionWidth); + Vector3f vboNwVec = new Vector3f(vboCenterVec.x() - halfRegionWidth, vboCenterVec.y(), vboCenterVec.z() - halfRegionWidth); + Vector3f vboNeVec = new Vector3f(vboCenterVec.x() + halfRegionWidth, vboCenterVec.y(), vboCenterVec.z() - halfRegionWidth); // if any corner is visible, this region should be rendered return isNormalizedVectorInViewFrustum(vboSeVec, cameraDir) || @@ -113,7 +116,7 @@ public class RenderUtil * Currently takes the dot product of the two vectors, * but in the future could do more complicated frustum culling tests. */ - private static boolean isNormalizedVectorInViewFrustum(Vector3d objectVector, Vector3d cameraDir) + private static boolean isNormalizedVectorInViewFrustum(Vector3f objectVector, Vector3f cameraDir) { // the -0.1 is to offer a slight buffer, so we are // more likely to render LODs and thus, hopefully prevent diff --git a/src/main/java/com/seibel/lod/render/shader/LodShader.java b/src/main/java/com/seibel/lod/render/shader/LodShader.java new file mode 100644 index 000000000..d806b70ef --- /dev/null +++ b/src/main/java/com/seibel/lod/render/shader/LodShader.java @@ -0,0 +1,116 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.render.shader; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.lwjgl.opengl.GL20; + +import com.seibel.lod.proxy.ClientProxy; + +/** + * 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) throws Exception + { + StringBuilder stringBuilder = new StringBuilder(); + + try + { + // open the file + InputStream in = absoluteFilePath ? new FileInputStream(path) : LodShader.class.getClassLoader().getResourceAsStream(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) + { + ClientProxy.LOGGER.error("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) throws Exception + { + LodShader shader = new LodShader(type); + GL20.glShaderSource(shader.id, source); + shader.compile(); + + return shader; + } + + /** + * Compiles the shader and checks it's status afterwards. + * @throws Exception if the shader fails to compile + */ + public void compile() throws Exception + { + GL20.glCompileShader(id); + + // check if the shader compiled + int status = GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS); + if (status != GL20.GL_TRUE) + throw new Exception(GL20.glGetShaderInfoLog(id)); + } + +} diff --git a/src/main/java/com/seibel/lod/render/shader/LodShaderProgram.java b/src/main/java/com/seibel/lod/render/shader/LodShaderProgram.java new file mode 100644 index 000000000..df90cef5b --- /dev/null +++ b/src/main/java/com/seibel/lod/render/shader/LodShaderProgram.java @@ -0,0 +1,184 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.render.shader; + +import java.nio.FloatBuffer; + +import org.lwjgl.opengl.GL20; +import org.lwjgl.system.MemoryStack; + +import net.minecraft.util.math.vector.Matrix4f; + + +/** + * 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-8-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() throws Exception + { + GL20.glLinkProgram(this.id); + checkLinkStatus(); + } + + /** + * Checks if the program was linked successfully. + * @throws Exception if the program failed to link + */ + public void checkLinkStatus() throws Exception + { + int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS); + if (status != GL20.GL_TRUE) + throw new Exception(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 an 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); + } + + /** + * Sets the uniform variable for specified location. + * + * @param location Uniform location + * @param value Value to set + */ + public void setUniform(int location, int value) + { + GL20.glUniform1i(location, value); + } + + /** + * Sets the uniform variable for specified location. + * + * @param location Uniform location + * @param value Value to set + */ + public void setUniform(int location, Matrix4f value) + { + try (MemoryStack stack = MemoryStack.stackPush()) + { + FloatBuffer buffer = stack.mallocFloat(4 * 4); + value.store(buffer); + GL20.glUniformMatrix4fv(location, false, buffer); + } + } + + + +} diff --git a/src/main/java/com/seibel/lod/util/LodUtil.java b/src/main/java/com/seibel/lod/util/LodUtil.java index 30e132379..ae3bd4f79 100644 --- a/src/main/java/com/seibel/lod/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/util/LodUtil.java @@ -29,6 +29,8 @@ import com.seibel.lod.enums.HorizontalResolution; import com.seibel.lod.enums.VanillaOverdraw; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.RegionPos; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; +import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; import com.seibel.lod.wrappers.MinecraftWrapper; import net.minecraft.client.multiplayer.ServerData; @@ -349,10 +351,10 @@ public class LodUtil * Get a HashSet of all ChunkPos within the normal render distance * that should not be rendered. */ - public static HashSet getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos) + public static HashSet getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPosWrapper blockPosWrapper) { int chunkRenderDist = mc.getRenderDistance(); - ChunkPos centerChunk = new ChunkPos(playerPos); + ChunkPosWrapper centerChunk = new ChunkPosWrapper(blockPosWrapper); int skipRadius; VanillaOverdraw overdraw = LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get(); @@ -426,12 +428,12 @@ public class LodUtil // if the skipRadius is being used if (skipRadius != 0) { - for (int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++) + for (int x = centerChunk.getX() - chunkRenderDist; x < centerChunk.getX() + chunkRenderDist; x++) { - for (int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++) + for (int z = centerChunk.getZ() - chunkRenderDist; z < centerChunk.getZ() + chunkRenderDist; z++) { - if (x <= centerChunk.x - skipRadius || x >= centerChunk.x + skipRadius - || z <= centerChunk.z - skipRadius || z >= centerChunk.z + skipRadius) + if (x <= centerChunk.getX() - skipRadius || x >= centerChunk.getX() + skipRadius + || z <= centerChunk.getZ() - skipRadius || z >= centerChunk.getZ() + skipRadius) posToSkip.remove(new ChunkPos(x, z)); } diff --git a/src/main/java/com/seibel/lod/util/ThreadMapUtil.java b/src/main/java/com/seibel/lod/util/ThreadMapUtil.java index bf19076f3..9eca7fbf6 100644 --- a/src/main/java/com/seibel/lod/util/ThreadMapUtil.java +++ b/src/main/java/com/seibel/lod/util/ThreadMapUtil.java @@ -19,8 +19,6 @@ package com.seibel.lod.util; -import static com.seibel.lod.util.LodUtil.DETAIL_OPTIONS; - import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -80,7 +78,7 @@ public class ThreadMapUtil || (adjDataMap.get(Thread.currentThread().getName()).get(Direction.NORTH) == null) || (adjDataMap.get(Thread.currentThread().getName()).get(Direction.NORTH).length != verticalData)) { - adjDataMap.put(Thread.currentThread().getName(), new HashMap()); + adjDataMap.put(Thread.currentThread().getName(), new HashMap<>()); adjDataMap.get(Thread.currentThread().getName()).put(Direction.UP, new long[1]); adjDataMap.get(Thread.currentThread().getName()).put(Direction.DOWN, new long[1]); for (Direction direction : Box.ADJ_DIRECTIONS) @@ -142,9 +140,9 @@ public class ThreadMapUtil { if (!saveContainer.containsKey(Thread.currentThread().getName()) || (saveContainer.get(Thread.currentThread().getName()) == null)) { - byte[][] array = new byte[DETAIL_OPTIONS][]; + byte[][] array = new byte[LodUtil.DETAIL_OPTIONS][]; int size = 1; - for (int i = DETAIL_OPTIONS - 1; i >= 0; i--) + for (int i = LodUtil.DETAIL_OPTIONS - 1; i >= 0; i--) { array[i] = new byte[2 + 8 * size * size * DetailDistanceUtil.getMaxVerticalData(i)]; size = size << 1; @@ -188,8 +186,8 @@ public class ThreadMapUtil { if (!verticalUpdate.containsKey(Thread.currentThread().getName()) || (verticalUpdate.get(Thread.currentThread().getName()) == null)) { - long[][] array = new long[DETAIL_OPTIONS][]; - for (int i = 1; i < DETAIL_OPTIONS; i++) + 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); } diff --git a/src/main/java/com/seibel/lod/wrappers/Block/BlockColorWrapper.java b/src/main/java/com/seibel/lod/wrappers/Block/BlockColorWrapper.java index ee082fef5..a45a6da4e 100644 --- a/src/main/java/com/seibel/lod/wrappers/Block/BlockColorWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/Block/BlockColorWrapper.java @@ -22,7 +22,7 @@ public class BlockColorWrapper //set of block which require tint public static final ConcurrentMap blockColorWrapperMap = new ConcurrentHashMap<>(); public static final ModelDataMap dataMap = new ModelDataMap.Builder().build(); - public static final BlockPos blockPos = new BlockPos(0,0,0); + public static final BlockPos blockPos = new BlockPos(0, 0, 0); public static final Random random = new Random(0); //public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER); public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN }; @@ -37,33 +37,51 @@ public class BlockColorWrapper /**Constructor only require for the block instance we are wrapping**/ - public BlockColorWrapper(BlockState blockState, BlockPosWrapper blockPosWrapper) + public BlockColorWrapper(Block block) { - this.block = blockState.getBlock(); + this.block = block; this.color = 0; this.isColored = true; this.toTint = false; this.foliageTint = false; this.grassTint = false; this.waterTint = false; - setupColorAndTint(blockState,blockPosWrapper); + setupColorAndTint(); + /*StringBuilder s = new StringBuilder(); + s.append(block + "\n" + + Integer.toHexString( + Minecraft.getInstance().getBlockColors().createDefault().getColor( + block.defaultBlockState(), + (World) MinecraftWrapper.INSTANCE.getWrappedServerLevel().getLevel(), + blockPosWrapper.getBlockPos())) + "\n" + ); + for(Property x : Minecraft.getInstance().getBlockColors().getColoringProperties(block)) + s.append(x.getName() + " " + x.getPossibleValues() + '\n'); + System.out.println(s);*/ //System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint); } /** - * this return a wrapper of the block in input - * @param blockState of the block to wrap + * return base color of water (grey value) */ - static public BlockColorWrapper getBlockColorWrapper(BlockState blockState, BlockPosWrapper blockPosWrapper) + static public BlockColorWrapper getWaterColor() + { + return getBlockColorWrapper(Blocks.WATER); + } + /** + * this return a wrapper of the block in input + * @param block object of the block to wrap + */ + static public BlockColorWrapper getBlockColorWrapper(Block block) { //first we check if the block has already been wrapped - if (blockColorWrapperMap.containsKey(blockState.getBlock()) && blockColorWrapperMap.get(blockState.getBlock()) != null) - return blockColorWrapperMap.get(blockState.getBlock()); + if (blockColorWrapperMap.containsKey(block) && blockColorWrapperMap.get(block) != null) + return blockColorWrapperMap.get(block); //if it hasn't been created yet, we create it and save it in the map - BlockColorWrapper blockWrapper = new BlockColorWrapper(blockState, blockPosWrapper); - blockColorWrapperMap.put(blockState.getBlock(), blockWrapper); + BlockColorWrapper blockWrapper = new BlockColorWrapper(block); + blockColorWrapperMap.put(block, blockWrapper); //we return the newly created wrapper return blockWrapper; @@ -73,8 +91,10 @@ public class BlockColorWrapper * Generate the color of the given block from its texture * and store it for later use. */ - private void setupColorAndTint(BlockState blockState, BlockPosWrapper blockPosWrapper) + private void setupColorAndTint() { + BlockState blockState = block.defaultBlockState(); + BlockPosWrapper blockPosWrapper = new BlockPosWrapper(); MinecraftWrapper mc = MinecraftWrapper.INSTANCE; TextureAtlasSprite texture; List quads = null; @@ -114,7 +134,7 @@ public class BlockColorWrapper else { isColored = true; - texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientWorld(), blockPosWrapper.getBlockPos()); + texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientLevel(), blockPosWrapper.getBlockPos()); } int count = 0; @@ -229,7 +249,7 @@ public class BlockColorWrapper public int getColor() { - return color; + return color; } //------------// diff --git a/src/main/java/com/seibel/lod/wrappers/Block/BlockPosWrapper.java b/src/main/java/com/seibel/lod/wrappers/Block/BlockPosWrapper.java index 7332a8a22..a5e89fd81 100644 --- a/src/main/java/com/seibel/lod/wrappers/Block/BlockPosWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/Block/BlockPosWrapper.java @@ -1,64 +1,67 @@ package com.seibel.lod.wrappers.Block; -import com.seibel.lod.util.ColorUtil; -import net.minecraft.block.*; -import net.minecraft.client.renderer.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -import net.minecraftforge.client.model.data.ModelDataMap; - -import java.util.List; import java.util.Objects; -import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; + +public class BlockPosWrapper { + private final BlockPos.Mutable blockPos; -//This class wraps the minecraft BlockPos.Mutable (and BlockPos) class -public class BlockPosWrapper -{ - private final BlockPos.Mutable blockPos; - - - public BlockPosWrapper() - { - this.blockPos = new BlockPos.Mutable(0,0,0); - } - - public void set(int x, int y, int z) - { - blockPos.set(x, y, z); - } - - public int getX() - { - return blockPos.getX(); - } - - public int getY() - { - return blockPos.getY(); - } - - public int getZ() - { - return blockPos.getZ(); - } - - public BlockPos.Mutable getBlockPos() - { - return blockPos; - } - - @Override public boolean equals(Object o) - { - return blockPos.equals(o); - } - - @Override public int hashCode() - { - return Objects.hash(blockPos); - } - + public BlockPosWrapper() + { + this.blockPos = new BlockPos.Mutable(0,0,0); + } + + public BlockPosWrapper(int x, int y, int z) + { + this.blockPos = new BlockPos.Mutable(x, y, z); + } + + public void set(int x, int y, int z) + { + blockPos.set(x, y, z); + } + + public int getX() + { + return blockPos.getX(); + } + + public int getY() + { + return blockPos.getY(); + } + + public int getZ() + { + return blockPos.getZ(); + } + + public int get(Direction.Axis axis) + { + return axis.choose(getX(), getY(), getZ()); + } + + public BlockPos.Mutable getBlockPos() + { + return blockPos; + } + + @Override public boolean equals(Object o) + { + return blockPos.equals(o); + } + + @Override public int hashCode() + { + return Objects.hash(blockPos); + } + + public BlockPosWrapper offset(int x, int y, int z) + { + blockPos.set(blockPos.getX() + x, blockPos.getY() + y, blockPos.getZ() + z); + return this; + } + } diff --git a/src/main/java/com/seibel/lod/wrappers/Block/BlockShapeWrapper.java b/src/main/java/com/seibel/lod/wrappers/Block/BlockShapeWrapper.java index df3f9e599..88f17f17d 100644 --- a/src/main/java/com/seibel/lod/wrappers/Block/BlockShapeWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/Block/BlockShapeWrapper.java @@ -23,7 +23,7 @@ public class BlockShapeWrapper public static BlockShapeWrapper WATER_SHAPE = new BlockShapeWrapper(); private final Block block; - private final boolean toAvoid; + private boolean toAvoid; private boolean nonFull; private boolean noCollision; diff --git a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkPosWrapper.java b/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkPosWrapper.java index ed93576ce..2e6ff6723 100644 --- a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkPosWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkPosWrapper.java @@ -1,5 +1,7 @@ package com.seibel.lod.wrappers.Chunk; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; + import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; @@ -11,9 +13,30 @@ public class ChunkPosWrapper { private final ChunkPos chunkPos; - public ChunkPosWrapper(ChunkPos chunkPos) + public ChunkPosWrapper(ChunkPos newChunkPos) + { + this.chunkPos = newChunkPos; + } + + public ChunkPosWrapper(BlockPos blockPos) + { + this.chunkPos = new ChunkPos(blockPos); + } + + + public ChunkPosWrapper(ChunkPosWrapper newChunkPos) + { + this.chunkPos = newChunkPos.chunkPos; + } + + public ChunkPosWrapper(BlockPosWrapper blockPos) { - this.chunkPos = chunkPos; + this.chunkPos = new ChunkPos(blockPos.getBlockPos()); + } + + public ChunkPosWrapper(int chunkX, int chunkZ) + { + this.chunkPos = new ChunkPos(chunkX, chunkZ); } public int getX() @@ -61,4 +84,9 @@ public class ChunkPosWrapper return Objects.hash(chunkPos); } + public BlockPosWrapper getWorldPosition() + { + BlockPos blockPos = chunkPos.getWorldPosition(); + return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ()); + } } diff --git a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkWrapper.java b/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkWrapper.java index 078778493..6f36a5500 100644 --- a/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/Chunk/ChunkWrapper.java @@ -26,9 +26,8 @@ public class ChunkWrapper BlockState blockState = chunk.getBlockState(blockPos.getBlockPos()); //This type of block is always in water - //This type of block could be in water return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable)) - || (blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get()); + || (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)); } public int getHeightMapValue(int xRel, int zRel){ @@ -42,7 +41,7 @@ public class ChunkWrapper public BlockColorWrapper getBlockColorWrapper(BlockPosWrapper blockPos) { - return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(blockPos.getBlockPos()),blockPos); + return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(blockPos.getBlockPos()).getBlock()); } public BlockShapeWrapper getBlockShapeWrapper(BlockPosWrapper blockPos) @@ -73,9 +72,8 @@ public class ChunkWrapper BlockState blockState = chunk.getBlockState(blockPos.getBlockPos()); //This type of block is always in water - //This type of block could be in water return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable)) - || (blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get()); + || (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)); } public int getEmittedBrightness(BlockPosWrapper blockPos) diff --git a/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java b/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java index 8c7c2fcd6..70bfb4d23 100644 --- a/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java @@ -26,7 +26,9 @@ import com.seibel.lod.ModInfo; import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.World.WorldWrapper; +import com.seibel.lod.wrappers.Block.BlockPosWrapper; +import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper; +import com.seibel.lod.wrappers.World.LevelWrapper; import net.minecraft.client.GameSettings; import net.minecraft.client.MainWindow; import net.minecraft.client.Minecraft; @@ -44,6 +46,7 @@ import net.minecraft.entity.Entity; import net.minecraft.profiler.IProfiler; import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.DimensionType; import net.minecraft.world.server.ServerWorld; @@ -170,6 +173,17 @@ public class MinecraftWrapper return mc.player; } + public BlockPosWrapper getPlayerBlockPos() + { + BlockPos playerPos = getPlayer().blockPosition(); + return new BlockPosWrapper(playerPos.getX(), playerPos.getY(), playerPos.getZ()); + } + + public ChunkPosWrapper getPlayerChunkPos() + { + return new ChunkPosWrapper(getPlayer().xChunk, getPlayer().zChunk); + } + public GameSettings getOptions() { return mc.options; @@ -180,17 +194,17 @@ public class MinecraftWrapper return mc.getModelManager(); } - public ClientWorld getClientWorld() + public ClientWorld getClientLevel() { return mc.level; } - public WorldWrapper getWrappedClientWorld() + public LevelWrapper getWrappedClientLevel() { - return WorldWrapper.getWorldWrapper(mc.level); + return LevelWrapper.getLevelWrapper(mc.level); } - public WorldWrapper getWrappedServerWorld() + public LevelWrapper getWrappedServerLevel() { if (mc.level == null) @@ -212,7 +226,7 @@ public class MinecraftWrapper } } - return WorldWrapper.getWorldWrapper(returnWorld); + return LevelWrapper.getLevelWrapper(returnWorld); } /** Measured in chunks */ diff --git a/src/main/java/com/seibel/lod/wrappers/World/BiomeColorWrapper.java b/src/main/java/com/seibel/lod/wrappers/World/BiomeColorWrapper.java index 9ec7b0072..15762db9c 100644 --- a/src/main/java/com/seibel/lod/wrappers/World/BiomeColorWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/World/BiomeColorWrapper.java @@ -1,33 +1,24 @@ package com.seibel.lod.wrappers.World; -import com.seibel.lod.util.LodUtil; import com.seibel.lod.wrappers.Block.BlockPosWrapper; -import net.minecraft.block.Blocks; -import net.minecraft.block.material.MaterialColor; -import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.BiomeColors; -import java.awt.*; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - public class BiomeColorWrapper { - public static int getGrassColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper) + public static int getGrassColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper) { - return BiomeColors.getAverageGrassColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos()); + return BiomeColors.getAverageGrassColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos()); } - public static int getWaterColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper) + public static int getWaterColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper) { - return BiomeColors.getAverageWaterColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos()); + return BiomeColors.getAverageWaterColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos()); } - public static int getFoliageColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper) + public static int getFoliageColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper) { - return BiomeColors.getAverageFoliageColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos()); + return BiomeColors.getAverageFoliageColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos()); } } diff --git a/src/main/java/com/seibel/lod/wrappers/World/BiomeWrapper.java b/src/main/java/com/seibel/lod/wrappers/World/BiomeWrapper.java index 05d4552e3..8e513a848 100644 --- a/src/main/java/com/seibel/lod/wrappers/World/BiomeWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/World/BiomeWrapper.java @@ -1,16 +1,10 @@ package com.seibel.lod.wrappers.World; import com.seibel.lod.util.ColorUtil; -import com.seibel.lod.util.LodUtil; import com.seibel.lod.wrappers.Block.BlockColorWrapper; -import com.seibel.lod.wrappers.Block.BlockPosWrapper; import net.minecraft.block.Blocks; -import net.minecraft.block.material.MaterialColor; -import net.minecraft.world.World; import net.minecraft.world.biome.Biome; -import net.minecraft.world.biome.BiomeColors; -import java.awt.*; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -48,57 +42,72 @@ public class BiomeWrapper public int getColorForBiome(int x, int z) { int colorInt; + int tintValue = 0; switch (biome.getBiomeCategory()) { case NETHER: - colorInt = Blocks.NETHERRACK.defaultBlockState().materialColor.col; + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.NETHERRACK).getColor(); break; case THEEND: - colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col; + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.END_STONE).getColor(); break; case BEACH: case DESERT: - colorInt = Blocks.SAND.defaultBlockState().materialColor.col; + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SAND).getColor(); break; case EXTREME_HILLS: - colorInt = Blocks.STONE.defaultMaterialColor().col; + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.STONE).getColor(); break; case MUSHROOM: - colorInt = MaterialColor.COLOR_LIGHT_GRAY.col; + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.MYCELIUM).getColor(); break; case ICY: - colorInt = Blocks.SNOW.defaultMaterialColor().col; + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SNOW).getColor(); break; case MESA: - colorInt = Blocks.RED_SAND.defaultMaterialColor().col; + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.RED_SAND).getColor(); break; case OCEAN: case RIVER: - colorInt = biome.getWaterColor(); + colorInt = BlockColorWrapper.getWaterColor().getColor(); + tintValue = biome.getWaterColor(); + break; + + case PLAINS: + case SAVANNA: + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.GRASS_BLOCK).getColor(); + tintValue = biome.getGrassColor(x, z); + colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue); + break; + + case TAIGA: + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SPRUCE_LEAVES).getColor(); + tintValue = biome.getFoliageColor(); + colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue); + break; + case JUNGLE: + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.JUNGLE_LEAVES).getColor(); + tintValue = biome.getFoliageColor(); + colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue); break; case NONE: - case FOREST: - case TAIGA: - case JUNGLE: - case PLAINS: - case SAVANNA: - case SWAMP: default: - Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z)); - tmp = tmp.darker(); - colorInt = LodUtil.colorToInt(tmp); + case SWAMP: + case FOREST: + colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.OAK_LEAVES).getColor(); + tintValue = biome.getFoliageColor(); + colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue); break; - } return colorInt; diff --git a/src/main/java/com/seibel/lod/wrappers/World/LevelWrapper.java b/src/main/java/com/seibel/lod/wrappers/World/LevelWrapper.java new file mode 100644 index 000000000..17c35c352 --- /dev/null +++ b/src/main/java/com/seibel/lod/wrappers/World/LevelWrapper.java @@ -0,0 +1,80 @@ +package com.seibel.lod.wrappers.World; + +import com.seibel.lod.wrappers.Block.BlockPosWrapper; +import net.minecraft.world.IWorld; +import net.minecraft.world.LightType; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class LevelWrapper +{ + private static final ConcurrentMap worldWrapperMap = new ConcurrentHashMap<>(); + private final IWorld world; + + public LevelWrapper(IWorld world) + { + this.world = world; + } + + + public static LevelWrapper getLevelWrapper(IWorld world) + { + //first we check if the biome has already been wrapped + if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null) + return worldWrapperMap.get(world); + + + //if it hasn't been created yet, we create it and save it in the map + LevelWrapper levelWrapper = new LevelWrapper(world); + worldWrapperMap.put(world, levelWrapper); + + //we return the newly created wrapper + return levelWrapper; + } + + public static void clearMap() + { + worldWrapperMap.clear(); + } + + public DimensionTypeWrapper getDimensionType() + { + return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType()); + } + + public int getBlockLight(BlockPosWrapper blockPos) + { + return world.getBrightness(LightType.BLOCK, blockPos.getBlockPos()); + } + + public int getSkyLight(BlockPosWrapper blockPos) + { + return world.getBrightness(LightType.SKY, blockPos.getBlockPos()); + } + + public BiomeWrapper getBiome(BlockPosWrapper blockPos) + { + return BiomeWrapper.getBiomeWrapper(world.getBiome(blockPos.getBlockPos())); + } + + public IWorld getWorld() + { + return world; + } + + public boolean hasCeiling() + { + return world.dimensionType().hasCeiling(); + } + + public boolean hasSkyLight() + { + return world.dimensionType().hasSkyLight(); + } + + public boolean isEmpty() + { + return world == null; + } +} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index ec77a4159..585a847ec 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -38,6 +38,9 @@ displayURL="https://www.curseforge.com/minecraft/mc-mods/lod-level-of-detail" #o #// 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 @@ -45,4 +48,4 @@ credits="TechnoVision, Vike, and Darkhax for their modding tutorials." #optional 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.''' \ No newline at end of file +description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.''' diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png new file mode 100644 index 000000000..6e8dd25f1 Binary files /dev/null and b/src/main/resources/icon.png differ diff --git a/src/main/resources/shaders/unshaded.frag b/src/main/resources/shaders/unshaded.frag new file mode 100644 index 000000000..4119de8b8 --- /dev/null +++ b/src/main/resources/shaders/unshaded.frag @@ -0,0 +1,26 @@ +#version 150 core + +in vec4 vertexColor; +//in vec2 textureCoord; + +out vec4 fragColor; + +//uniform sampler2D texImage; + + +/** + * Fragment Shader + * + * author: James Seibel + * version: 11-8-2021 + */ +void main() +{ + // TODO: add a white texture to support Optifine shaders + //vec4 textureColor = texture(texImage, textureCoord); + //fragColor = vertexColor * textureColor; + + + // very simple fragment shader, just return the vertix's color + fragColor = vertexColor; +} diff --git a/src/main/resources/shaders/unshaded.vert b/src/main/resources/shaders/unshaded.vert new file mode 100644 index 000000000..20b4e88f1 --- /dev/null +++ b/src/main/resources/shaders/unshaded.vert @@ -0,0 +1,29 @@ +#version 150 core + +in vec3 vPosition; +in vec4 color; + +out vec4 vertexColor; +//out vec2 textureCoord; + +uniform mat4 modelViewMatrix; +uniform mat4 projectionMatrix; + + +/** + * Vertex Shader + * + * author: James Seibel + * version: 11-8-2021 + */ +void main() +{ + // TODO: add a simple white texture to support Optifine shaders + //textureCoord = textureCoord; + + vertexColor = color; + + // 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); +}