diff --git a/build.gradle b/build.gradle index 64ad3ee48..eb06014f2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,6 @@ buildscript { repositories { - maven { url = 'https://files.minecraftforge.net/maven' } - jcenter() + maven { url = 'https://files.minecraftforge.net' } mavenCentral() // potential replacement in case of problems: // https://dist.creeper.host/Sponge/maven @@ -19,20 +18,13 @@ apply plugin: 'org.spongepowered.mixin' // Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. apply plugin: 'eclipse' apply plugin: 'maven-publish' -apply plugin: 'java' // needed for compileJava version = 'a1.4.1' group = 'com.seibel.lod' archivesBaseName = 'lod_1.16.5' -sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. -compileJava { - // release 8 is needed because otherwise FloatBuffer.flip() will crash - // on some machines - // example thread: https://github.com/eclipse/jetty.project/issues/3244 - options.compilerArgs.addAll(['-Xlint:unchecked', '-Xlint:deprecation']) -} +sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) minecraft { @@ -200,7 +192,7 @@ jar { "Specification-Title": "LOD", "Specification-Version": "1", // We are version 1 of ourselves "Implementation-Title": project.name, - "Implementation-Version": "{version}", + "Implementation-Version": "1", "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), "MixinConfigs": "lod.mixins.json", ]) @@ -228,4 +220,4 @@ publishing { mixin { add sourceSets.main, "lod.refmap.json" -} +} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/Main.java b/src/main/java/com/seibel/lod/Main.java index 05c00a423..1619bf7fe 100644 --- a/src/main/java/com/seibel/lod/Main.java +++ b/src/main/java/com/seibel/lod/Main.java @@ -1,11 +1,12 @@ package com.seibel.lod; -import com.google.common.primitives.UnsignedLong; -import com.seibel.lod.objects.PosToGenerateContainer; +import java.util.HashMap; +import java.util.Map; + +import com.seibel.lod.builders.lodTemplates.Box; import com.seibel.lod.util.DataPointUtil; -import javax.xml.crypto.Data; -import java.math.BigInteger; +import net.minecraft.util.Direction; public class Main { @@ -13,18 +14,39 @@ public class Main { try { - long[][] dataToMerge = new long[][]{ - {DataPointUtil.createDataPoint(10, 5, 0, 0, 0, 0)}, - {DataPointUtil.createDataPoint(15, 5, 0, 0, 0, 0)}, - {DataPointUtil.createDataPoint(40, 20, 0, 0, 0, 0)}, - {DataPointUtil.createDataPoint(1, 0, 0, 0, 0, 0)}}; - long[] data = DataPointUtil.mergeVerticalData(dataToMerge); - for (long dataPoint : data) + @SuppressWarnings("serial") + Map adjData = new HashMap() + {{ + put(Direction.EAST, new long[]{DataPointUtil.createDataPoint(60, 30, 0, 0, 0, 0), DataPointUtil.createDataPoint(25, 10, 0, 0, 0, 0)}); + put(Direction.WEST, new long[]{DataPointUtil.createDataPoint(60, 10, 0, 0, 0, 0)}); + put(Direction.NORTH, new long[]{DataPointUtil.createDataPoint(40, 20, 0, 0, 0, 0)}); + put(Direction.SOUTH, new long[]{DataPointUtil.createDataPoint(40, 20, 0, 0, 0, 0)}); + }}; + + Box box = new Box(); + int height = 60; + int depth = 20; + + box.set(10, height - depth, 10); + box.move(0, depth, 0); + box.setAdjData(adjData); + + for(Direction direction : Box.DIRECTIONS) { - System.out.println("depth " + DataPointUtil.getDepth(dataPoint)); - System.out.println("height " + DataPointUtil.getHeight(dataPoint)); + int adjIndex = 0; + while (box.shouldContinue(direction, adjIndex)) + { + System.out.println(direction.toString()); + for (int i = 0; i < 4; i++) + { + System.out.println(box.getX(direction, i) + " " + box.getY(direction, i, adjIndex) + " " + box.getZ(direction, i)); + } + adjIndex++; + } } - }catch (Exception e){ + + } catch (Exception e) + { e.printStackTrace(); } } diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java index 7ab1a5cab..6f595a3f1 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -19,7 +19,9 @@ package com.seibel.lod.builders; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -48,6 +50,7 @@ import com.seibel.lod.util.LodUtil; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.vertex.VertexBuffer; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; @@ -110,9 +113,9 @@ public class LodBufferBuilder */ private ReentrantLock bufferLock = new ReentrantLock(); - private static final int NUMBER_OF_DIRECTION = 4; +// private static final int NUMBER_OF_DIRECTION = 4; //in order -x, +x, -z, +z - private static final int[][] ADJ_DIRECTION = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; +// private static final int[][] ADJ_VECTOR = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; private volatile Box[][] boxCache; private volatile PosToRenderContainer[][] setsToRender; @@ -169,7 +172,7 @@ public class LodBufferBuilder long startTime = System.currentTimeMillis(); - ArrayList> nodeToRenderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); + ArrayList> nodeToRenderThreads = new ArrayList<>(lodDim.getWidth() * lodDim.getWidth()); startBuffers(fullRegen, lodDim); @@ -182,25 +185,25 @@ public class LodBufferBuilder center = playerRegionPos; if (setsToRender == null) - setsToRender = new PosToRenderContainer[lodDim.regions.length][lodDim.regions.length]; + setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()]; - if (setsToRender.length != lodDim.regions.length) - setsToRender = new PosToRenderContainer[lodDim.regions.length][lodDim.regions.length]; + if (setsToRender.length != lodDim.getWidth()) + setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()]; if (boxCache == null) - boxCache = new Box[lodDim.regions.length][lodDim.regions.length]; + boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()]; - if (boxCache.length != lodDim.regions.length) - boxCache = new Box[lodDim.regions.length][lodDim.regions.length]; + if (boxCache.length != lodDim.getWidth()) + boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()]; // this will be the center of the VBOs once they have been built buildableCenterChunkPos = playerChunkPos; - for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++) + for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++) { - for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++) + for (int zRegion = 0; zRegion < lodDim.getWidth(); zRegion++) { - if (lodDim.regen[xRegion][zRegion] || fullRegen) + if (lodDim.getRegenByArrayIndex(xRegion, zRegion) || fullRegen) { RegionPos regionPos = new RegionPos( xRegion + lodDim.getCenterX() - Math.floorDiv(lodDim.getWidth(), 2), @@ -220,7 +223,14 @@ public class LodBufferBuilder final int zR = zRegion; Callable dataToRenderThread = () -> { - + Map adjData = new HashMap<>(); + if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) + { + adjData.put(Direction.WEST, new long[1]); + adjData.put(Direction.EAST, new long[1]); + adjData.put(Direction.SOUTH, new long[1]); + adjData.put(Direction.NORTH, new long[1]); + } //previous setToRender chache if (setsToRender[xR][zR] == null) { @@ -247,10 +257,12 @@ public class LodBufferBuilder int zAdj; int chunkXdist; int chunkZdist; - short gameChunkRenderDistance = (short) (renderer.vanillaRenderedChunks.length / 2 - 1); - //long dataPoint; - long[] adjData = new long[NUMBER_OF_DIRECTION]; - + + // keep a local version so we don't have to worry about indexOutOfBounds Exceptions + // if it changes in the LodRenderer while we are working here + boolean[][] vanillaRenderedChunks = renderer.vanillaRenderedChunks; + short gameChunkRenderDistance = (short) (vanillaRenderedChunks.length / 2 - 1); + for (int index = 0; index < posToRender.getNumberOfPos(); index++) { detailLevel = posToRender.getNthDetailLevel(index); @@ -262,63 +274,84 @@ public class LodBufferBuilder if (gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist) && detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL - && renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]) + && vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]) { continue; } // skip any chunks that Minecraft is going to render try { + for (Direction direction : Box.ADJ_DIRECTIONS) + { + xAdj = posX + direction.getNormal().getX(); + zAdj = posZ + direction.getNormal().getZ(); + chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.x; + chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.z; + if (gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist)) + { + if (!vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] + && posToRender.contains(detailLevel, xAdj, zAdj)) + { + if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) + { + adjData.get(direction)[0] = lodDim.getSingleData(detailLevel, xAdj, zAdj); + } else + { + adjData.put(direction, lodDim.getData(detailLevel, xAdj, zAdj)); + } + } else + { + if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) + { + adjData.get(direction)[0] = DataPointUtil.createVoidDataPoint(0); + } else + { + adjData.put(direction, null); + } + } + } else + { + if (posToRender.contains(detailLevel, xAdj, zAdj)) + { + if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) + { + adjData.get(direction)[0] = lodDim.getSingleData(detailLevel, xAdj, zAdj); + } else + { + adjData.put(direction, lodDim.getData(detailLevel, xAdj, zAdj)); + } + } else + { + + if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == LodQualityMode.HEIGHTMAP) + { + adjData.get(direction)[0] = DataPointUtil.createVoidDataPoint(0); + } else + { + adjData.put(direction, null); + } + } + } + } if (region.getLodQualityMode() == LodQualityMode.HEIGHTMAP) { //dataPoint = lodDim.getData(detailLevel, posX, posZ)[0]; long dataPoint = lodDim.getSingleData(detailLevel, posX, posZ); if (!DataPointUtil.isItVoid(dataPoint) && DataPointUtil.doesItExist(dataPoint)) { - dataPoint = lodDim.getSingleData(detailLevel, posX, posZ); - if(DataPointUtil.getHeight(dataPoint) == LodBuilder.DEFAULT_HEIGHT && DataPointUtil.getDepth(dataPoint) == LodBuilder.DEFAULT_DEPTH) - continue; - for (int direction = 0; direction < NUMBER_OF_DIRECTION; direction++) - { - - xAdj = posX + ADJ_DIRECTION[direction][0]; - zAdj = posZ + ADJ_DIRECTION[direction][1]; - chunkXdist = LevelPosUtil.getChunkPos(detailLevel,xAdj) - playerChunkPos.x; - chunkZdist = LevelPosUtil.getChunkPos(detailLevel,zAdj) - playerChunkPos.z; - - if (gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist)) - { - if (!renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1] - && posToRender.contains(detailLevel, xAdj, zAdj)) - { - adjData[direction]= lodDim.getSingleData(detailLevel, xAdj, zAdj); - }else{ - adjData[direction]= 0; - } - } else - { - if (posToRender.contains(detailLevel, xAdj, zAdj)) - { - adjData[direction] = lodDim.getSingleData(detailLevel, xAdj, zAdj); - }else{ - adjData[direction]= 0; - } - } - } LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPosRounded, dataPoint, adjData, - detailLevel, posX, posZ, boxCache[xR][zR],renderer.previousDebugMode, renderer.lightMap); + detailLevel, posX, posZ, boxCache[xR][zR], renderer.previousDebugMode, renderer.lightMap); } } else if (region.getLodQualityMode() == LodQualityMode.MULTI_LOD) { - //dataPoint = lodDim.getData(detailLevel, posX, posZ)[0]; for (long dataPoint : lodDim.getData(detailLevel, posX, posZ)) { if (!DataPointUtil.isItVoid(dataPoint) && DataPointUtil.doesItExist(dataPoint)) { LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPosRounded, dataPoint, adjData, - detailLevel, posX, posZ, boxCache[xR][zR], renderer.previousDebugMode, renderer.lightMap); + detailLevel, posX, posZ, boxCache[xR][zR], renderer.previousDebugMode, renderer.lightMap); } } } @@ -366,13 +399,11 @@ public class LodBufferBuilder // mark that the buildable buffers as ready to swap switchVbos = true; - } - catch (Exception e) + } catch (Exception e) { ClientProxy.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: "); e.printStackTrace(); - } - finally + } finally { // regardless of if we successfully created the buffers // we are done generating. @@ -385,9 +416,6 @@ public class LodBufferBuilder // upload the new buffers uploadBuffers(fullRegen, lodDim); bufferLock.unlock(); - - // make sure the context is disabled - GlProxy.getInstance().setGlContext(GlProxyContext.NONE); } }); @@ -459,7 +487,7 @@ public class LodBufferBuilder { for (int z = 0; z < buildableBuffers.length; z++) { - if (fullRegen || lodDim.regen[x][z]) + if (fullRegen || lodDim.getRegenByArrayIndex(x, z)) { buildableBuffers[x][z].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT); } @@ -474,7 +502,7 @@ public class LodBufferBuilder { for (int x = 0; x < buildableBuffers.length; x++) for (int z = 0; z < buildableBuffers.length; z++) - if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building() && (fullRegen || lodDim.regen[x][z])) + if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building() && (fullRegen || lodDim.getRegenByArrayIndex(x, z))) buildableBuffers[x][z].end(); } @@ -483,73 +511,78 @@ public class LodBufferBuilder */ private void uploadBuffers(boolean fullRegen, LodDimension lodDim) { + GlProxy glProxy = GlProxy.getInstance(); + try { - GlProxy glProxy = GlProxy.getInstance(); // make sure we are uploading to a different OpenGL context, // to prevent interference (IE stuttering) with the Minecraft context. glProxy.setGlContext(GlProxyContext.LOD_BUILDER); - // only print console debugging for vboUpload once per upload cycle - boolean bufferMapFail = false; - - + + for (int x = 0; x < buildableVbos.length; x++) { for (int z = 0; z < buildableVbos.length; z++) { - if (fullRegen || lodDim.regen[x][z]) + if (fullRegen || lodDim.getRegenByArrayIndex(x, z)) { ByteBuffer builderBuffer = buildableBuffers[x][z].popNextBuffer().getSecond(); - bufferMapFail = vboUpload(buildableVbos[x][z], builderBuffer, bufferMapFail); - lodDim.regen[x][z] = false; + vboUpload(buildableVbos[x][z], builderBuffer); + lodDim.setRegenByArrayIndex(x, z, false); } } } - - + + // make sure all the buffers have been uploaded. // this probably is necessary, but it makes me feel good :) GL11.glFlush(); - glProxy.setGlContext(GlProxyContext.NONE); } - catch(IllegalStateException e) + catch (IllegalStateException e) { ClientProxy.LOGGER.error(LodBufferBuilder.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage()); e.printStackTrace(); } + finally + { + // make sure no buffer is bound + if (glProxy.getGlContext() == GlProxyContext.LOD_BUILDER) + { + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + + // make sure the context is disabled + glProxy.setGlContext(GlProxyContext.NONE); + } } /** * Uploads the uploadBuffer into the VBO in GPU memory. */ - private boolean vboUpload(VertexBuffer vbo, ByteBuffer uploadBuffer, boolean bufferMapFail) + private void vboUpload(VertexBuffer vbo, ByteBuffer uploadBuffer) { // this shouldn't happen, but just to be safe if (vbo.id != -1) { // this is how many points will be rendered vbo.vertexCount = (uploadBuffer.remaining() / vbo.format.getVertexSize()); - - + + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); - - + + // subData only works if the memory is allocated beforehand. GL15C.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer.remaining(), GL15C.GL_DYNAMIC_DRAW); - + // interestingly bufferSubData renders faster than glMapBuffer // even though OpenGLInsights-AsynchronousBufferTransfers says glMapBuffer // is faster for transferring data. They must put the data in different memory // or something. GL15C.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); - - - GL15C.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); + + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); } - - // just used to improve debug printing - return bufferMapFail; } /** diff --git a/src/main/java/com/seibel/lod/builders/LodBuilder.java b/src/main/java/com/seibel/lod/builders/LodBuilder.java index 6abaa8b8c..98d3857eb 100644 --- a/src/main/java/com/seibel/lod/builders/LodBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBuilder.java @@ -29,13 +29,26 @@ import com.seibel.lod.config.LodConfig; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.LodDetail; import com.seibel.lod.enums.LodQualityMode; -import com.seibel.lod.util.*; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.LodRegion; import com.seibel.lod.objects.LodWorld; +import com.seibel.lod.util.ColorUtil; +import com.seibel.lod.util.DataPointUtil; +import com.seibel.lod.util.DetailDistanceUtil; +import com.seibel.lod.util.LevelPosUtil; +import com.seibel.lod.util.LodThreadFactory; +import com.seibel.lod.util.LodUtil; +import com.seibel.lod.util.ThreadMapUtil; import com.seibel.lod.wrappers.MinecraftWrapper; -import net.minecraft.block.*; +import net.minecraft.block.AbstractPlantBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.BushBlock; +import net.minecraft.block.GrassBlock; +import net.minecraft.block.IGrowable; +import net.minecraft.block.LeavesBlock; import net.minecraft.block.material.MaterialColor; import net.minecraft.client.renderer.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -49,25 +62,24 @@ import net.minecraft.world.IWorld; import net.minecraft.world.LightType; import net.minecraft.world.World; import net.minecraft.world.biome.Biome; -import net.minecraft.world.biome.BiomeColors; import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.IChunk; import net.minecraft.world.gen.Heightmap; import net.minecraftforge.client.extensions.IForgeBakedModel; import net.minecraftforge.client.model.data.ModelDataMap; -import javax.swing.*; - /** * This object is in charge of creating Lod related objects. (specifically: Lod * World, Dimension, and Region objects) * * @author Leonardo Amato * @author James Seibel - * @version 9-7-2021 + * @version 9-18-2021 */ public class LodBuilder { + private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE; + private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName())); public static final int CHUNK_DATA_WIDTH = LodUtil.CHUNK_WIDTH; @@ -118,20 +130,25 @@ public class LodBuilder { try { + // we need a loaded client world in order to + // get the textures for blocks + if (mc.getClientWorld() == null) + return; + DimensionType dim = world.dimensionType(); LodDimension lodDim; int playerPosX; int playerPosZ; - if (MinecraftWrapper.INSTANCE.getPlayer() == null) + if (mc.getPlayer() == null) { playerPosX = chunk.getPos().getMinBlockX(); playerPosZ = chunk.getPos().getMinBlockZ(); } else { - playerPosX = (int) MinecraftWrapper.INSTANCE.getPlayer().getX(); - playerPosZ = (int) MinecraftWrapper.INSTANCE.getPlayer().getZ(); + playerPosX = (int) mc.getPlayer().getX(); + playerPosZ = (int) mc.getPlayer().getZ(); } if (lodWorld.getLodDimension(dim) == null) { @@ -181,10 +198,6 @@ public class LodBuilder int startZ; int endX; int endZ; - int color; - byte light; - short height; - short depth; try { LodDetail detail; @@ -228,7 +241,7 @@ public class LodBuilder case MULTI_LOD: long[][] dataToMergeVertical; dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ, endX, endZ); - data = DataPointUtil.mergeVerticalData(dataToMergeVertical); + data = DataPointUtil.mergeMultiData(dataToMergeVertical); if (data.length == 0 || data == null) data = new long[]{DataPointUtil.EMPTY_DATA}; lodDim.addData(detailLevel, @@ -269,7 +282,7 @@ public class LodBuilder int xAbs; int yAbs; int zAbs; - boolean hasCeiling = MinecraftWrapper.INSTANCE.getWorld().dimensionType().hasCeiling(); + boolean hasCeiling = mc.getClientWorld().dimensionType().hasCeiling(); BlockPos.Mutable blockPos = new BlockPos.Mutable(0, 0, 0); int index = 0; @@ -582,12 +595,12 @@ public class LodBuilder { int skyLight; int blockLight; - if (MinecraftWrapper.INSTANCE.getPlayer() == null) + if (mc.getPlayer() == null) return 0; - if (MinecraftWrapper.INSTANCE.getPlayer().level == null) + if (mc.getPlayer().level == null) return 0; - IWorld world = MinecraftWrapper.INSTANCE.getPlayer().level; + IWorld world = mc.getPlayer().level; blockLight = world.getBrightness(LightType.BLOCK, blockPos); skyLight = world.getBrightness(LightType.SKY, blockPos); @@ -610,23 +623,23 @@ public class LodBuilder return colorMap.get(blockState.getBlock()); - World world = MinecraftWrapper.INSTANCE.getWorld(); + World world = mc.getClientWorld(); TextureAtlasSprite texture; if(topTextureRequired) { - List quad = ((IForgeBakedModel) MinecraftWrapper.INSTANCE.getModelManager().getBlockModelShaper().getBlockModel(blockState)).getQuads(blockState, Direction.UP, new Random(0), dataMap); + List quad = ((IForgeBakedModel) mc.getModelManager().getBlockModelShaper().getBlockModel(blockState)).getQuads(blockState, Direction.UP, new Random(0), dataMap); if (!quad.isEmpty()) { texture = quad.get(0).getSprite(); } else { - texture = MinecraftWrapper.INSTANCE.getModelManager().getBlockModelShaper().getTexture(blockState, world, blockPos); + texture = mc.getModelManager().getBlockModelShaper().getTexture(blockState, world, blockPos); } } else { - texture = MinecraftWrapper.INSTANCE.getModelManager().getBlockModelShaper().getTexture(blockState, world, blockPos); + texture = mc.getModelManager().getBlockModelShaper().getTexture(blockState, world, blockPos); } @@ -688,15 +701,13 @@ public class LodBuilder int z = blockPos.getZ(); Biome biome = chunk.getBiomes().getNoiseBiome(xRel >> 2, y >> 2, zRel >> 2); int brightness; - int red = 0; - int green = 0; - int blue = 0; BlockState blockState = chunk.getBlockState(blockPos); int colorInt = 0; // block special cases + // TODO: this needs to be replaced by a config file of some sort if (blockState == Blocks.AIR.defaultBlockState() || blockState == Blocks.CAVE_AIR.defaultBlockState() || blockState == Blocks.BARRIER.defaultBlockState()) @@ -720,7 +731,7 @@ public class LodBuilder { colorInt = Blocks.NETHER_WART_BLOCK.defaultMaterialColor().col; } else if (blockState.getBlock().equals(Blocks.TWISTING_VINES) - || blockState.equals(Blocks.TWISTING_VINES_PLANT) + || blockState.equals(Blocks.TWISTING_VINES_PLANT.defaultBlockState()) || blockState == Blocks.WARPED_ROOTS.defaultBlockState() || blockState == Blocks.WARPED_FUNGUS.defaultBlockState() || blockState == Blocks.NETHER_SPROUTS.defaultBlockState()) diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java index 7384e8695..f40c79d5f 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java @@ -17,13 +17,15 @@ */ package com.seibel.lod.builders.lodTemplates; +import java.util.Map; + import com.seibel.lod.enums.DebugMode; +import com.seibel.lod.util.ColorUtil; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.DimensionType; /** * This is the abstract class used to create different @@ -36,7 +38,7 @@ public abstract class AbstractLodTemplate { - public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData, + public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map adjData, byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap); /** @@ -44,10 +46,10 @@ public abstract class AbstractLodTemplate */ protected void addPosAndColor(BufferBuilder buffer, double x, double y, double z, - int red, int green, int blue, int alpha) + int color) { - buffer.vertex(x, y, z).color(red, green, blue, alpha).endVertex(); + buffer.vertex(x, y, z).color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), ColorUtil.getAlpha(color)).endVertex(); } /** diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/Box.java b/src/main/java/com/seibel/lod/builders/lodTemplates/Box.java index 500e6bd16..46840a8db 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/Box.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/Box.java @@ -1,13 +1,19 @@ package com.seibel.lod.builders.lodTemplates; +import java.util.HashMap; +import java.util.Map; + +import com.seibel.lod.config.LodConfig; +import com.seibel.lod.enums.DebugMode; +import com.seibel.lod.util.ColorUtil; +import com.seibel.lod.util.DataPointUtil; +import com.seibel.lod.wrappers.MinecraftWrapper; + +import net.minecraft.util.Direction; +import net.minecraft.util.math.vector.Vector3d; + public class Box { - public static final int DOWN = 0; - public static final int UP = 1; - public static final int EAST = 2; - public static final int WEST = 3; - public static final int SOUTH = 4; - public static final int NORTH = 5; public static final int OFFSET = 0; public static final int WIDTH = 1; @@ -16,49 +22,404 @@ public class Box public static final int Y = 1; public static final int Z = 2; + public static final int MIN = 0; + public static final int MAX = 1; + + public static final int VOID_FACE = 0; + + public static final Direction[] DIRECTIONS = new Direction[]{ + Direction.UP, + Direction.DOWN, + Direction.WEST, + Direction.EAST, + Direction.NORTH, + Direction.SOUTH}; + + public static final Direction[] ADJ_DIRECTIONS = new Direction[]{ + Direction.EAST, + Direction.WEST, + Direction.SOUTH, + Direction.NORTH}; + + @SuppressWarnings("serial") + public static final Map DIRECTION_VERTEX_MAP = new HashMap() + {{ + put(Direction.UP, new int[][]{ + {0, 1, 0}, + {0, 1, 1}, + {1, 1, 1}, + {1, 1, 0}}); + put(Direction.DOWN, new int[][]{ + {1, 0, 0}, + {1, 0, 1}, + {0, 0, 1}, + {0, 0, 0}}); + put(Direction.EAST, new int[][]{ + {1, 1, 0}, + {1, 1, 1}, + {1, 0, 1}, + {1, 0, 0}}); + put(Direction.WEST, new int[][]{ + {0, 0, 0}, + {0, 0, 1}, + {0, 1, 1}, + {0, 1, 0}}); + put(Direction.SOUTH, new int[][]{ + {1, 0, 1}, + {1, 1, 1}, + {0, 1, 1}, + {0, 0, 1}}); + put(Direction.NORTH, new int[][]{ + {0, 0, 0}, + {0, 1, 0}, + {1, 1, 0}, + {1, 0, 0}}); + }}; + + + @SuppressWarnings("serial") + public static final Map FACE_DIRECTION = new HashMap() + {{ + put(Direction.UP, new int[]{Y, MAX}); + put(Direction.DOWN, new int[]{Y, MIN}); + put(Direction.EAST, new int[]{X, MAX}); + put(Direction.WEST, new int[]{X, MIN}); + put(Direction.SOUTH, new int[]{Z, MAX}); + put(Direction.NORTH, new int[]{Z, MIN}); + }}; + + @SuppressWarnings("serial") + public static final Map DIRECTION_NORMAL_MAP = new HashMap() + {{ + put(Direction.UP, new int[]{0, 1, 0}); + put(Direction.DOWN, new int[]{0, -1, 0}); + put(Direction.EAST, new int[]{1, 0, 0}); + put(Direction.WEST, new int[]{-1, 0, 0}); + put(Direction.SOUTH, new int[]{0, 0, 1}); + put(Direction.NORTH, new int[]{0, 0, -1}); + }}; + public int[][] box; + public Map colorMap; + public int color; + public Map adjHeightAndDepth; + public Map culling; - public Box(){ + @SuppressWarnings("serial") + public Box() + { box = new int[2][3]; + //order = new long[32]; + colorMap = new HashMap() + {{ + put(Direction.UP, new int[1]); + put(Direction.DOWN, new int[1]); + put(Direction.EAST, new int[1]); + put(Direction.WEST, new int[1]); + put(Direction.SOUTH, new int[1]); + put(Direction.NORTH, new int[1]); + }}; + adjHeightAndDepth = new HashMap() + {{ + put(Direction.EAST, new int[32][2]); + put(Direction.WEST, new int[32][2]); + put(Direction.SOUTH, new int[32][2]); + put(Direction.NORTH, new int[32][2]); + }}; + culling = new HashMap() + {{ + put(Direction.UP, new boolean[1]); + put(Direction.DOWN, new boolean[1]); + put(Direction.EAST, new boolean[1]); + put(Direction.WEST, new boolean[1]); + put(Direction.SOUTH, new boolean[1]); + put(Direction.NORTH, new boolean[1]); + }}; } - public void set(int xWidth, int yWidth, int zWidth){ - box[OFFSET][X] = 0; - box[OFFSET][Y] = 0; - box[OFFSET][Z] = 0; - - box[WIDTH][X] = xWidth; - box[WIDTH][Y] = yWidth; - box[WIDTH][Z] = zWidth; + public void setColor(int color) + { + this.color = color; + for (Direction direction : DIRECTIONS) + { + colorMap.get(direction)[0] = ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true)); + } } - public void move(int xOffset, int yOffset, int zOffset){ + public int getColor(Direction direction) + { + if (LodConfig.CLIENT.debugging.debugMode.get() != DebugMode.SHOW_DETAIL) + { + return colorMap.get(direction)[0]; + } else + { + return color; + } + } + + public void reset() + { + for (int i = 0; i < box.length; i++) + { + for (int j = 0; j < box[i].length; j++) + { + box[i][j] = 0; + } + } + + for (Direction direction : DIRECTIONS) + { + colorMap.get(direction)[0] = 0; + } + + for (Direction direction : ADJ_DIRECTIONS) + { + if(isCulled(direction)){ + continue; + } + for (int i = 0; i < adjHeightAndDepth.get(direction).length; i++) + { + adjHeightAndDepth.get(direction)[i][0] = VOID_FACE; + adjHeightAndDepth.get(direction)[i][1] = VOID_FACE; + } + } + } + + public void setUpCulling(int cullingDistance) + { + Vector3d playerPos = MinecraftWrapper.INSTANCE.getPlayer().position(); + for (Direction direction : DIRECTIONS) + { + if(direction == Direction.DOWN) + culling.get(direction)[0] = playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance; + else if(direction == Direction.UP) + culling.get(direction)[0] = playerPos.get(direction.getAxis()) < getFacePos(direction) - cullingDistance; + else if(direction == Direction.WEST) + culling.get(direction)[0] = -playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance; + else if(direction == Direction.NORTH) + culling.get(direction)[0] = -playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance; + else if(direction == Direction.EAST) + culling.get(direction)[0] = -playerPos.get(direction.getAxis()) < getFacePos(direction) - cullingDistance; + else if(direction == Direction.SOUTH) + culling.get(direction)[0] = -playerPos.get(direction.getAxis()) < getFacePos(direction) - cullingDistance; + } + } + + public boolean isCulled(Direction direction) + { + return culling.get(direction)[0]; + } + + public void setAdjData(Map adjData) + { + int height; + int depth; + int minY = getMinY(); + int maxY = getMaxY(); + for (Direction direction : ADJ_DIRECTIONS) + { + //if(isCulled(direction)){ + // continue; + //} + + long[] dataPoint = adjData.get(direction); + if (dataPoint == null || DataPointUtil.isItVoid(dataPoint[0])) + { + adjHeightAndDepth.get(direction)[0][0] = maxY; + adjHeightAndDepth.get(direction)[0][1] = minY; + adjHeightAndDepth.get(direction)[1][0] = VOID_FACE; + adjHeightAndDepth.get(direction)[1][1] = VOID_FACE; + continue; + } + + //We order the adj list + /**TODO remove this if the order is maintained naturally*/ + /* + order[0] = 0; + for (int i = 0; i < dataPoint.length; i++) + { + int j = i - 1; + while (j >= 0 && DataPointUtil.getHeight(order[j]) > DataPointUtil.getHeight(dataPoint[i])) { + order[j + 1] = order[j]; + j = j - 1; + } + order[j + 1] = dataPoint[i]; + }*/ + + int i; + int faceToDraw = 0; + boolean firstFace = true; + boolean toFinish = false; + for (i = dataPoint.length - 1; i >= 0; i--) + { + long singleDataPoint = dataPoint[i]; + height = DataPointUtil.getHeight(singleDataPoint); + depth = DataPointUtil.getDepth(singleDataPoint); + + if (depth > maxY) + {//the adj data is higher than the current data + //we continue since there could be some other data that intersect the current + continue; + } else if (height < minY) + {//the adj data is lower than the current data + //we break since all the other data will be lower + + if (firstFace) + { + adjHeightAndDepth.get(direction)[0][0] = getMaxY(); + adjHeightAndDepth.get(direction)[0][1] = getMinY(); + } else + { + adjHeightAndDepth.get(direction)[faceToDraw][1] = getMinY(); + } + faceToDraw++; + toFinish = false; + break; + } else if (depth <= minY && height >= maxY) + {//the adj data contains the current + //we do not draw the face + adjHeightAndDepth.get(direction)[0][0] = VOID_FACE; + adjHeightAndDepth.get(direction)[0][1] = VOID_FACE; + break; + } else if (depth <= minY && height < maxY) + {//the adj data intersect the lower part of the current data + //if this is the only face we use the maxY and break + //if there was other face we finish the last one and break + if (firstFace) + { + adjHeightAndDepth.get(direction)[0][0] = getMaxY(); + adjHeightAndDepth.get(direction)[0][1] = height; + } else + { + adjHeightAndDepth.get(direction)[faceToDraw][1] = height; + } + toFinish = false; + faceToDraw++; + break; + } else if (depth > minY && height >= maxY) + {//the adj data intersect the higher part of the current data + //we start the creation of a new face + adjHeightAndDepth.get(direction)[faceToDraw][0] = depth; + firstFace = false; + toFinish = true; + continue; + } else if (depth > minY && height < maxY) + {//the adj data is contained in the current data + if (firstFace) + { + adjHeightAndDepth.get(direction)[0][0] = getMaxY(); + } + adjHeightAndDepth.get(direction)[faceToDraw][1] = height; + faceToDraw++; + adjHeightAndDepth.get(direction)[faceToDraw][0] = depth; + firstFace = false; + toFinish = true; + continue; + } + } + if (toFinish) + { + adjHeightAndDepth.get(direction)[faceToDraw][1] = minY; + faceToDraw++; + } + adjHeightAndDepth.get(direction)[faceToDraw][0] = VOID_FACE; + adjHeightAndDepth.get(direction)[faceToDraw][1] = VOID_FACE; + } + } + + public void set(int xWidth, int yWidth, int zWidth) + { + box[WIDTH][X] = xWidth; + box[WIDTH][Y] = yWidth; + box[WIDTH][Z] = zWidth; + } + + public void move(int xOffset, int yOffset, int zOffset) + { box[OFFSET][X] = xOffset; box[OFFSET][Y] = yOffset; box[OFFSET][Z] = zOffset; } - public int getMinX(){ + + + public int getFacePos(Direction direction) + { + return box[OFFSET][FACE_DIRECTION.get(direction)[0]] + box[WIDTH][FACE_DIRECTION.get(direction)[0]] * FACE_DIRECTION.get(direction)[1]; + } + + public int getCoord(Direction direction, int axis, int vertexIndex) + { + return box[OFFSET][axis] + box[WIDTH][axis] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][axis]; + } + + public int getX(Direction direction, int vertexIndex) + { + return box[OFFSET][X] + box[WIDTH][X] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][X]; + } + + public int getY(Direction direction, int vertexIndex) + { + return box[OFFSET][Y] + box[WIDTH][Y] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y]; + } + + public int getY(Direction direction, int vertexIndex, int adjIndex) + { + if (direction == Direction.DOWN || direction == Direction.UP) + { + return box[OFFSET][Y] + box[WIDTH][Y] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y]; + } else + { + return adjHeightAndDepth.get(direction)[adjIndex][1 - DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y]]; + } + } + + public int getZ(Direction direction, int vertexIndex) + { + return box[OFFSET][Z] + box[WIDTH][Z] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Z]; + } + + public boolean shouldContinue(Direction direction, int adjIndex) + { + if (direction == Direction.UP || direction == Direction.DOWN) + { + if (adjIndex == 0) + return true; + else + return false; + } + return !(adjHeightAndDepth.get(direction)[adjIndex][0] == VOID_FACE && adjHeightAndDepth.get(direction)[adjIndex][1] == VOID_FACE); + + } + + public int getMinX() + { return box[OFFSET][X]; } - public int getMaxX(){ + public int getMaxX() + { return box[OFFSET][X] + box[WIDTH][X]; } - public int getMinY(){ + public int getMinY() + { return box[OFFSET][Y]; } - public int getMaxY(){ + public int getMaxY() + { return box[OFFSET][Y] + box[WIDTH][Y]; } - public int getMinZ(){ + public int getMinZ() + { return box[OFFSET][Z]; } - public int getMaxZ(){ + public int getMaxZ() + { return box[OFFSET][Z] + box[WIDTH][Z]; } + } diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java index 3dcc85828..14fb8a10d 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java @@ -17,24 +17,16 @@ */ package com.seibel.lod.builders.lodTemplates; -import com.seibel.lod.config.LodConfig; +import java.util.Map; + import com.seibel.lod.enums.DebugMode; -import com.seibel.lod.enums.ShadingMode; -import com.seibel.lod.objects.LodDimension; import com.seibel.lod.util.DataPointUtil; -import com.seibel.lod.util.ColorUtil; import com.seibel.lod.util.LodUtil; -import com.seibel.lod.wrappers.MinecraftWrapper; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.DimensionType; - -import java.lang.annotation.Native; /** * Builds LODs as rectangular prisms. @@ -44,20 +36,27 @@ import java.lang.annotation.Native; */ public class CubicLodTemplate extends AbstractLodTemplate { - private final int CULL_OFFSET = 16; - + public CubicLodTemplate() { } @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map adjData, byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap) { int width = 1 << detailLevel; + int color = DataPointUtil.getLightColor(data, lightMap); // add each LOD for the detail level + + if (debugging != DebugMode.OFF) + + { + color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB(); + } + generateBoundingBox( box, DataPointUtil.getHeight(data), @@ -66,28 +65,19 @@ public class CubicLodTemplate extends AbstractLodTemplate posX * width, 0, posZ * width, - bufferCenterBlockPos); - int color; - color = DataPointUtil.getLightColor(data,lightMap); - - //color = DataPointUtil.getColor(data); - - - if (debugging != DebugMode.OFF) - - { - color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB(); - } + bufferCenterBlockPos, + adjData, + color); if (box != null) { - addBoundingBoxToBuffer(buffer, box, color, bufferCenterBlockPos, adjData); + addBoundingBoxToBuffer(buffer, box); } } - private void generateBoundingBox(Box box, int height, int depth, int width, double xOffset, double yOffset, double zOffset, BlockPos bufferCenterBlockPos) + private void generateBoundingBox(Box box, int height, int depth, int width, double xOffset, double yOffset, double zOffset, BlockPos bufferCenterBlockPos, Map adjData, int color) { // don't add an LOD if it is empty if (height == -1 && depth == -1) @@ -105,285 +95,34 @@ public class CubicLodTemplate extends AbstractLodTemplate // which only uses floats double x = -bufferCenterBlockPos.getX(); double z = -bufferCenterBlockPos.getZ(); + box.reset(); + box.setColor(color); box.set(width, height - depth, width); - box.move((int) (xOffset + x), (int) (yOffset + depth), (int) (zOffset + z)); + box.move((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z)); + box.setUpCulling(32); + box.setAdjData(adjData); } - private void addBoundingBoxToBuffer(BufferBuilder buffer, Box box, int c, BlockPos playerBlockPos, long[] adjData) + private void addBoundingBoxToBuffer(BufferBuilder buffer, Box box) { - int topColor = c; - int bottomColor = c; - int northColor = c; - int southColor = c; - int westColor = c; - int eastColor = c; - - // darken the bottom and side colors if requested - if (LodConfig.CLIENT.graphics.shadingMode.get() == ShadingMode.DARKEN_SIDES) + for(Direction direction : Box.DIRECTIONS) { - // the side colors are different because - // when using fast lighting in Minecraft the north/south - // and east/west sides are different in a similar way - /**TODO OPTIMIZE THIS STEP*/ - Minecraft mc = Minecraft.getInstance(); - topColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.UP, true)); - bottomColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.DOWN, true)); - northColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.NORTH, true)); - southColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.SOUTH, true)); - westColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.WEST, true)); - eastColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.EAST, true)); - } - - // apply the user specified saturation and brightness - float saturationMultiplier = LodConfig.CLIENT.graphics.saturationMultiplier.get().floatValue(); - float brightnessMultiplier = LodConfig.CLIENT.graphics.brightnessMultiplier.get().floatValue(); - - if (saturationMultiplier != 1 || brightnessMultiplier != 1) - { - topColor = ColorUtil.applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier); - bottomColor = ColorUtil.applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier); - northColor = ColorUtil.applySaturationAndBrightnessMultipliers(northColor, saturationMultiplier, brightnessMultiplier); - southColor = ColorUtil.applySaturationAndBrightnessMultipliers(southColor, saturationMultiplier, brightnessMultiplier); - westColor = ColorUtil.applySaturationAndBrightnessMultipliers(westColor, saturationMultiplier, brightnessMultiplier); - eastColor = ColorUtil.applySaturationAndBrightnessMultipliers(eastColor, saturationMultiplier, brightnessMultiplier); - } - int minX; - int maxX; - int minY; - int maxY; - int minZ; - int maxZ; - long data; - int tempMinY; - int tempMaxY; - - int red; - int green; - int blue; - int alpha; - boolean disableCulling = true; - /**TODO make all of this more automatic if possible*/ - if (playerBlockPos.getY() > box.getMaxY() - CULL_OFFSET || disableCulling) - { - red = ColorUtil.getRed(topColor); - green = ColorUtil.getGreen(topColor); - blue = ColorUtil.getBlue(topColor); - alpha = ColorUtil.getAlpha(topColor); - // top (facing up) - minX = box.getMinX(); - maxX = box.getMaxX(); - minY = box.getMinY(); - maxY = box.getMaxY(); - minZ = box.getMinZ(); - maxZ = box.getMaxZ(); - addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha); - } - if (playerBlockPos.getY() < box.getMinY() + CULL_OFFSET || disableCulling) - { - red = ColorUtil.getRed(bottomColor); - green = ColorUtil.getGreen(bottomColor); - blue = ColorUtil.getBlue(bottomColor); - alpha = ColorUtil.getAlpha(bottomColor); - // bottom (facing down) - minX = box.getMinX(); - maxX = box.getMaxX(); - minY = box.getMinY(); - maxY = box.getMaxY(); - minZ = box.getMinZ(); - maxZ = box.getMaxZ(); - addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha); - } - - if (playerBlockPos.getX() < box.getMaxX() + CULL_OFFSET || disableCulling) - { - red = ColorUtil.getRed(westColor); - green = ColorUtil.getGreen(westColor); - blue = ColorUtil.getBlue(westColor); - alpha = ColorUtil.getAlpha(westColor); - // west (facing -X) - data = adjData[0]; - - minX = box.getMinX(); - maxX = box.getMaxX(); - minY = box.getMinY(); - maxY = box.getMaxY(); - minZ = box.getMinZ(); - maxZ = box.getMaxZ(); - if (data == 0) + //if(box.isCulled(direction)) + // continue; + int adjIndex = 0; + while(box.shouldContinue(direction, adjIndex)) { - addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha); - }else - { - maxY = box.getMaxY(); - tempMaxY = DataPointUtil.getHeight(data); - if (tempMaxY < maxY) + for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++) { - minY = Math.max(tempMaxY, minY); - addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha); - } - tempMinY = DataPointUtil.getDepth(data); - minY = box.getMinY(); - if (tempMinY > minY) - { - maxY = Math.min(tempMinY, maxY); - addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha); + addPosAndColor(buffer, + box.getX(direction, vertexIndex), + box.getY(direction, vertexIndex, adjIndex), + box.getZ(direction, vertexIndex), + box.getColor(direction)); } + adjIndex++; } } - - if (playerBlockPos.getX() > box.getMinX() - CULL_OFFSET || disableCulling) - { - red = ColorUtil.getRed(eastColor); - green = ColorUtil.getGreen(eastColor); - blue = ColorUtil.getBlue(eastColor); - alpha = ColorUtil.getAlpha(eastColor); - // east (facing +X) - data = adjData[1]; - minX = box.getMinX(); - maxX = box.getMaxX(); - minY = box.getMinY(); - maxY = box.getMaxY(); - minZ = box.getMinZ(); - maxZ = box.getMaxZ(); - if (data == 0) - { - addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha); - } - else - { - maxY = box.getMaxY(); - tempMaxY = DataPointUtil.getHeight(data); - if (tempMaxY < maxY) - { - minY = Math.max(tempMaxY, minY); - addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha); - } - tempMinY = DataPointUtil.getDepth(data); - minY = box.getMinY(); - if (tempMinY > minY) - { - maxY = Math.min(tempMinY, maxY); - addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha); - } - } - } - - if (playerBlockPos.getZ() > box.getMinZ() - CULL_OFFSET || disableCulling) - { - red = ColorUtil.getRed(northColor); - green = ColorUtil.getGreen(northColor); - blue = ColorUtil.getBlue(northColor); - alpha = ColorUtil.getAlpha(northColor); - data = adjData[3]; - minX = box.getMinX(); - maxX = box.getMaxX(); - minY = box.getMinY(); - maxY = box.getMaxY(); - minZ = box.getMinZ(); - maxZ = box.getMaxZ(); - // north (facing +Z) - if (data == 0) - { - addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha); - } - else - { - maxY = box.getMaxY(); - tempMaxY = DataPointUtil.getHeight(data); - if (tempMaxY < maxY) - { - minY = Math.max(tempMaxY, minY); - addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha); - } - tempMinY = DataPointUtil.getDepth(data); - minY = box.getMinY(); - if (tempMinY > minY) - { - maxY = Math.min(tempMinY, maxY); - addPosAndColor(buffer, maxX, minY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, maxZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, minY, maxZ, red, green, blue, alpha); - } - } - } - - if (playerBlockPos.getZ() < box.getMaxZ() + CULL_OFFSET || disableCulling) - { - red = ColorUtil.getRed(southColor); - green = ColorUtil.getGreen(southColor); - blue = ColorUtil.getBlue(southColor); - alpha = ColorUtil.getAlpha(southColor); - data = adjData[2]; - minX = box.getMinX(); - maxX = box.getMaxX(); - minY = box.getMinY(); - maxY = box.getMaxY(); - minZ = box.getMinZ(); - maxZ = box.getMaxZ(); - // south (facing -Z) - if (data == 0) - { - addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha); - } - else - { - maxY = box.getMaxY(); - tempMaxY = DataPointUtil.getHeight(data); - if (tempMaxY < maxY) - { - minY = Math.max(tempMaxY, minY); - addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha); - } - tempMinY = DataPointUtil.getDepth(data); - minY = box.getMinY(); - if (tempMinY > minY) - { - maxY = Math.min(tempMinY, maxY); - addPosAndColor(buffer, minX, minY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, minX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, maxY, minZ, red, green, blue, alpha); - addPosAndColor(buffer, maxX, minY, minZ, red, green, blue, alpha); - } - } - } - } @Override diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java index a0d6460b8..d53fb9f6c 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/DynamicLodTemplate.java @@ -17,12 +17,15 @@ */ package com.seibel.lod.builders.lodTemplates; +import java.util.Map; + import com.seibel.lod.enums.DebugMode; +import com.seibel.lod.proxy.ClientProxy; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.texture.NativeImage; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.DimensionType; /** * TODO DynamicLodTemplate @@ -36,10 +39,10 @@ import net.minecraft.world.DimensionType; public class DynamicLodTemplate extends AbstractLodTemplate { @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map adjData, byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap) { - System.err.println("DynamicLodTemplate not implemented!"); + ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!"); } @Override diff --git a/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java b/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java index 2adebf3d7..9bfd8c920 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/TriangularLodTemplate.java @@ -17,12 +17,15 @@ */ package com.seibel.lod.builders.lodTemplates; +import java.util.Map; + import com.seibel.lod.enums.DebugMode; +import com.seibel.lod.proxy.ClientProxy; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.texture.NativeImage; +import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.DimensionType; /** * TODO #21 TriangularLodTemplate @@ -34,10 +37,10 @@ import net.minecraft.world.DimensionType; public class TriangularLodTemplate extends AbstractLodTemplate { @Override - public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, long[] adjData, + public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map adjData, byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap) { - System.err.println("DynamicLodTemplate not implemented!"); + ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!"); } @Override diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java index ffd5ea5b1..29d3edc00 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.function.Supplier; import com.google.common.util.concurrent.ThreadFactoryBuilder; diff --git a/src/main/java/com/seibel/lod/config/LodConfig.java b/src/main/java/com/seibel/lod/config/LodConfig.java index 987a25bae..b3d0d111a 100644 --- a/src/main/java/com/seibel/lod/config/LodConfig.java +++ b/src/main/java/com/seibel/lod/config/LodConfig.java @@ -20,13 +20,21 @@ package com.seibel.lod.config; import java.nio.file.Path; import java.nio.file.Paths; -import com.seibel.lod.enums.*; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.electronwill.nightconfig.core.io.WritingMode; import com.seibel.lod.ModInfo; +import com.seibel.lod.enums.DebugMode; +import com.seibel.lod.enums.DistanceCalculatorType; +import com.seibel.lod.enums.DistanceGenerationMode; +import com.seibel.lod.enums.FogDistance; +import com.seibel.lod.enums.FogDrawOverride; +import com.seibel.lod.enums.LodDetail; +import com.seibel.lod.enums.LodQualityMode; +import com.seibel.lod.enums.LodTemplate; +import com.seibel.lod.enums.ShadingMode; import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -37,7 +45,7 @@ import net.minecraftforge.fml.config.ModConfig; * This handles any configuration the user has access to. * * @author James Seibel - * @version 9-1-2021 + * @version 9-17-2021 */ @Mod.EventBusSubscriber public class LodConfig @@ -87,6 +95,8 @@ public class LodConfig public ForgeConfigSpec.IntValue lodChunkRenderDistance; + public ForgeConfigSpec.BooleanValue disableDirectionalCulling; + public ForgeConfigSpec.DoubleValue brightnessMultiplier; public ForgeConfigSpec.DoubleValue saturationMultiplier; @@ -150,6 +160,18 @@ public class LodConfig + " This is the render distance of the mod \n") .defineInRange("lodChunkRenderDistance", 64, 32, 512); + disableDirectionalCulling = builder + .comment("\n\n" + + " If false LODs that are behind the player's camera \n" + + " aren't drawn, increasing performance. \n\n" + + "" + + " If true all LODs are drawn, even those behind \n" + + " the player's camera, decreasing performance. \n\n" + + "" + + " Disable this if you see LODs disapearing. \n" + + " (This may happen if you are using a camera mod) \n") + .define("disableDirectionalCulling", false); + shadingMode = builder .comment("\n\n" + " What kind of shading should the LODs have? \n" diff --git a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java index 95536420c..429627f07 100644 --- a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java +++ b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java @@ -17,7 +17,14 @@ */ package com.seibel.lod.handlers; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.concurrent.ExecutorService; @@ -25,7 +32,10 @@ import java.util.concurrent.Executors; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.LodQualityMode; -import com.seibel.lod.objects.*; +import com.seibel.lod.objects.LodDimension; +import com.seibel.lod.objects.LodRegion; +import com.seibel.lod.objects.RegionPos; +import com.seibel.lod.objects.SingleLevelContainer; import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.util.LodThreadFactory; import com.seibel.lod.util.LodUtil; @@ -212,10 +222,10 @@ public class LodDimensionFileHandler { for (int j = 0; j < loadedDimension.getWidth(); j++) { - if (loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null) + if (loadedDimension.getRegenByArrayIndex(i,j) && loadedDimension.getRegionByArrayIndex(i,j) != null) { - saveRegionToFile(loadedDimension.regions[i][j]); - loadedDimension.isRegionDirty[i][j] = false; + saveRegionToFile(loadedDimension.getRegionByArrayIndex(i,j)); + loadedDimension.setRegenByArrayIndex(i, j,false); } } } diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java index 4229d16dd..c8d4353dc 100644 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodDimension.java @@ -26,7 +26,11 @@ import com.seibel.lod.config.LodConfig; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.LodQualityMode; import com.seibel.lod.handlers.LodDimensionFileHandler; -import com.seibel.lod.util.*; +import com.seibel.lod.util.DataPointUtil; +import com.seibel.lod.util.DetailDistanceUtil; +import com.seibel.lod.util.LevelPosUtil; +import com.seibel.lod.util.LodThreadFactory; +import com.seibel.lod.util.LodUtil; import com.seibel.lod.wrappers.MinecraftWrapper; import net.minecraft.util.math.ChunkPos; @@ -41,13 +45,13 @@ import net.minecraft.world.server.ServerWorld; * * @author Leonardo Amato * @author James Seibel - * @version 8-8-2021 + * @version 9-18-2021 */ public class LodDimension { - + public final DimensionType dimension; - + /** * measured in regions */ @@ -56,23 +60,28 @@ public class LodDimension * measured in regions */ private volatile int halfWidth; - - - public volatile LodRegion regions[][]; - public volatile boolean isRegionDirty[][]; - public volatile boolean regen[][]; + + // these three variables are private to force use of the getWidth() method + // which is a safer way to get the width then directly asking the arrays + /** stores all the regions in this dimension */ + private volatile LodRegion regions[][]; + /** stores if the region at the given x and z index needs to be saved to disk */ + private volatile boolean isRegionDirty[][]; + /** stores if the region at the given x and z index needs to be regenerated */ + private volatile boolean regionNeedsRegen[][]; + /** * if true that means there are regions in this dimension * that need to have their buffers rebuilt. */ public volatile boolean regenDimension = false; - + private volatile RegionPos center; private volatile ChunkPos lastGenChunk; private volatile ChunkPos lastCutChunk; private LodDimensionFileHandler fileHandler; private ExecutorService cutAndGenThreads = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - cutAndGen")); - + /** * Creates the dimension centered at (0,0) * @@ -90,14 +99,14 @@ public class LodDimension { try { - + File saveDir; if (mc.hasSingleplayerServer()) { // local world - + ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension); - + // provider needs a separate variable to prevent // the compiler from complaining ServerChunkProvider provider = serverWorld.getChunkSource(); @@ -105,11 +114,11 @@ public class LodDimension } else { // connected to server - + saveDir = new File(mc.getGameDirectory().getCanonicalFile().getPath() + - File.separatorChar + "lod server data" + File.separatorChar + mc.getCurrentDimensionId()); + File.separatorChar + "lod server data" + File.separatorChar + mc.getCurrentDimensionId()); } - + fileHandler = new LodDimensionFileHandler(saveDir, this); } catch (IOException e) { @@ -117,23 +126,23 @@ public class LodDimension // we won't be able to read or write any files } } - - + + regions = new LodRegion[width][width]; isRegionDirty = new boolean[width][width]; - regen = new boolean[width][width]; - + regionNeedsRegen = new boolean[width][width]; + //treeGenerator((int) mc.player.getX(),(int) mc.player.getZ()); - + // populate isRegionDirty for (int i = 0; i < width; i++) for (int j = 0; j < width; j++) isRegionDirty[i][j] = false; - + center = new RegionPos(0, 0); } - - + + /** * Move the center of this LodDimension and move all owned * regions over by the given x and z offset.

@@ -144,7 +153,7 @@ public class LodDimension { int xOffset = regionOffset.x; int zOffset = regionOffset.z; - + // if the x or z offset is equal to or greater than // the total size, just delete the current data // and update the centerX and/or centerZ @@ -157,15 +166,15 @@ public class LodDimension regions[x][z] = null; } } - + // update the new center center.x += xOffset; center.z += zOffset; - + return; } - - + + // X if (xOffset > 0) { @@ -196,8 +205,8 @@ public class LodDimension } } } - - + + // Z if (zOffset > 0) { @@ -226,22 +235,22 @@ public class LodDimension } } } - - + + // update the new center center.x += xOffset; center.z += zOffset; } - - + + /** - * return needed memory in byte + * return needed memory in bytes */ public int getMinMemoryNeeded() { int count = 0; LodRegion region; - + for (int x = 0; x < regions.length; x++) { for (int z = 0; z < regions.length; z++) @@ -255,8 +264,8 @@ public class LodDimension } return count; } - - + + /** * Gets the region at the given X and Z *
@@ -269,19 +278,19 @@ public class LodDimension int zRegion = LevelPosUtil.getRegion(detailLevel, posZ); int xIndex = (xRegion - center.x) + halfWidth; int zIndex = (zRegion - center.z) + halfWidth; - + if (!regionIsInRange(xRegion, zRegion)) return null; - //throw new ArrayIndexOutOfBoundsException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " out of range"); + //throw new ArrayIndexOutOfBoundsException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " out of range"); else if (regions[xIndex][zIndex] == null) return null; - //throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " not currently initialized"); + //throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " not currently initialized"); else if (regions[xIndex][zIndex].getMinDetailLevel() > detailLevel) return null; //throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel()); return regions[xIndex][zIndex]; } - + /** * Gets the region at the given X and Z *
@@ -292,16 +301,22 @@ public class LodDimension { int xIndex = (regionPosX - center.x) + halfWidth; int zIndex = (regionPosZ - center.z) + halfWidth; - + if (!regionIsInRange(regionPosX, regionPosZ)) return null; - //throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range"); + //throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range"); else if (regions[xIndex][zIndex] == null) return null; //throw new InvalidParameterException("Region " + regionPosX + " " + regionPosZ + " not currently initialized"); return regions[xIndex][zIndex]; } - + + /** Useful when needing to iterate over every region. */ + public LodRegion getRegionByArrayIndex(int xIndex, int zIndex) + { + return regions[xIndex][zIndex]; + } + /** * Overwrite the LodRegion at the location of newRegion with newRegion. * @@ -311,15 +326,15 @@ public class LodDimension { int xIndex = (newRegion.regionPosX - center.x) + halfWidth; int zIndex = (newRegion.regionPosZ - center.z) + halfWidth; - + if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ)) // out of range throw new ArrayIndexOutOfBoundsException("Region " + newRegion.regionPosX + ", " + newRegion.regionPosZ + " out of range"); - + regions[xIndex][zIndex] = newRegion; } - - + + /** * */ @@ -338,7 +353,7 @@ public class LodDimension int minDistance; byte detail; byte levelToCut; - + for (int x = 0; x < regions.length; x++) { for (int z = 0; z < regions.length; z++) @@ -358,15 +373,15 @@ public class LodDimension regions[x][z].cutTree(levelToCut); } } - + }// region z }// region z - + }); cutAndGenThreads.execute(thread); } } - + /** * */ @@ -375,7 +390,7 @@ public class LodDimension DistanceGenerationMode generationMode = LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get(); ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ)); LodQualityMode lodQualityMode = LodConfig.CLIENT.worldGenerator.lodQualityMode.get(); - + if (lastGenChunk == null) lastGenChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1); if (newPlayerChunk.x != lastGenChunk.x || newPlayerChunk.z != lastGenChunk.z) @@ -398,25 +413,25 @@ public class LodDimension final RegionPos regionPos = new RegionPos(regionX, regionZ); region = regions[x][z]; //We require that the region we are checking is loaded with at least this level - + minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ); detail = DetailDistanceUtil.getDistanceTreeGenInverse(minDistance); levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel; if (region == null || region.getGenerationMode() != generationMode) { //First case, region has to be initialized - + //We check if there is a file at the target level regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, lodQualityMode); - + //if there is no file we initialize the region if (regions[x][z] == null) { regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, lodQualityMode); } - regen[x][z] = true; + regionNeedsRegen[x][z] = true; regenDimension = true; - + } else if (region.getMinDetailLevel() > levelToGen) { //Second case, region has been initialized but at a higher level @@ -429,7 +444,7 @@ public class LodDimension cutAndGenThreads.execute(thread); } } - + /** * Add the given LOD to this dimension at the coordinate * stored in the LOD. If an LOD already exists at the given @@ -437,11 +452,11 @@ public class LodDimension */ public Boolean addData(byte detailLevel, int posX, int posZ, long[] dataPoint, boolean dontSave, boolean serverQuality) { - + // don't continue if the region can't be saved int regionPosX = LevelPosUtil.getRegion(detailLevel, posX); int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ); - + LodRegion region = getRegion(regionPosX, regionPosZ); if (region == null) return false; @@ -455,7 +470,7 @@ public class LodDimension int xIndex = (regionPosX - center.x) + halfWidth; int zIndex = (regionPosZ - center.z) + halfWidth; isRegionDirty[xIndex][zIndex] = true; - regen[xIndex][zIndex] = true; + regionNeedsRegen[xIndex][zIndex] = true; regenDimension = true; } catch (ArrayIndexOutOfBoundsException e) { @@ -466,8 +481,8 @@ public class LodDimension } return nodeAdded; } - - + + /** * Add the given LOD to this dimension at the coordinate * stored in the LOD. If an LOD already exists at the given @@ -475,11 +490,11 @@ public class LodDimension */ public Boolean addSingleData(byte detailLevel, int posX, int posZ, long dataPoint, boolean dontSave, boolean serverQuality) { - + // don't continue if the region can't be saved int regionPosX = LevelPosUtil.getRegion(detailLevel, posX); int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ); - + LodRegion region = getRegion(regionPosX, regionPosZ); if (region == null) return false; @@ -493,7 +508,7 @@ public class LodDimension int xIndex = (regionPosX - center.x) + halfWidth; int zIndex = (regionPosZ - center.z) + halfWidth; isRegionDirty[xIndex][zIndex] = true; - regen[xIndex][zIndex] = true; + regionNeedsRegen[xIndex][zIndex] = true; regenDimension = true; } catch (ArrayIndexOutOfBoundsException e) { @@ -504,14 +519,14 @@ public class LodDimension } return nodeAdded; } - + public void setToRegen(int xRegion, int zRegion) { int xIndex = (xRegion - center.x) + halfWidth; int zIndex = (zRegion - center.z) + halfWidth; - regen[xIndex][zIndex] = true; + regionNeedsRegen[xIndex][zIndex] = true; } - + /** * method to get all the quadtree level that have to be generated based on the position of the player * @@ -533,12 +548,12 @@ public class LodDimension region = getRegion(xIndex, zIndex); if (region != null) region.getDataToGenerate(posToGenerate, playerPosX, playerPosZ); - + } } return posToGenerate; } - + /** * method to get all the nodes that have to be rendered based on the position of the player * @@ -550,7 +565,7 @@ public class LodDimension if (region != null) region.getDataToRender(posToRender, playerPosX, playerPosZ); } - + /** * Get the data point at the given X and Z coordinates * in this dimension. @@ -562,17 +577,17 @@ public class LodDimension { if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); - + LodRegion region = getRegion(detailLevel, posX, posZ); - + if (region == null) { return new long[]{DataPointUtil.EMPTY_DATA}; } - + return region.getData(detailLevel, posX, posZ); } - + /** * Get the data point at the given X and Z coordinates * in this dimension. @@ -584,17 +599,28 @@ public class LodDimension { if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); - + LodRegion region = getRegion(detailLevel, posX, posZ); - + if (region == null) { return DataPointUtil.EMPTY_DATA; } - + return region.getSingleData(detailLevel, posX, posZ); } - + + + public boolean getRegenByArrayIndex(int xIndex, int zIndex) + { + return regionNeedsRegen[xIndex][zIndex]; + } + + public void setRegenByArrayIndex(int xIndex, int zIndex, boolean newRegen) + { + regionNeedsRegen[xIndex][zIndex] = newRegen; + } + /** * Get the data point at the given X and Z coordinates * in this dimension. @@ -606,32 +632,32 @@ public class LodDimension { if (detailLevel > LodUtil.REGION_DETAIL_LEVEL) throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max."); - + LodRegion region = getRegion(detailLevel, posX, posZ); - - + + if (region == null) { return; } region.updateArea(detailLevel, posX, posZ); } - + /** * return true if and only if the node at that position exist */ public boolean doesDataExist(byte detailLevel, int posX, int posZ) { LodRegion region = getRegion(detailLevel, posX, posZ); - + if (region == null) { return false; } - + return region.doesDataExist(detailLevel, posX, posZ); } - + /** * Get the region at the given X and Z coordinates from the * RegionFileHandler. @@ -643,7 +669,7 @@ public class LodDimension else return null; } - + /** * Save all dirty regions in this LodDimension to file. */ @@ -651,8 +677,8 @@ public class LodDimension { fileHandler.saveDirtyRegionsToFileAsync(); } - - + + /** * Returns whether the region at the given X and Z coordinates * is within the loaded range. @@ -661,22 +687,22 @@ public class LodDimension { int xIndex = (regionX - center.x) + halfWidth; int zIndex = (regionZ - center.z) + halfWidth; - + return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width; } - - + + public int getCenterX() { return center.x; } - + public int getCenterZ() { return center.z; } - - + + /** returns the width of the dimension in regions */ public int getWidth() { if (regions != null) @@ -690,28 +716,29 @@ public class LodDimension return width; } } - + + public void setRegionWidth(int newWidth) { width = newWidth; halfWidth = Math.floorDiv(width, 2); - + regions = new LodRegion[width][width]; isRegionDirty = new boolean[width][width]; - regen = new boolean[width][width]; - + regionNeedsRegen = new boolean[width][width]; + // populate isRegionDirty for (int i = 0; i < width; i++) for (int j = 0; j < width; j++) isRegionDirty[i][j] = false; } - - + + @Override public String toString() { LodRegion region; - + StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("Dimension : \n"); for (int x = 0; x < regions.length; x++) @@ -723,7 +750,7 @@ public class LodDimension { stringBuilder.append("n"); stringBuilder.append("\t"); - + } else { stringBuilder.append(region.getMinDetailLevel()); diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java index 2977bf5a4..ddadfb905 100644 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ b/src/main/java/com/seibel/lod/objects/LodRegion.java @@ -1,9 +1,9 @@ package com.seibel.lod.objects; -import com.seibel.lod.builders.LodBuilder; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.LodQualityMode; +import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.util.DataPointUtil; import com.seibel.lod.util.DetailDistanceUtil; import com.seibel.lod.util.LevelPosUtil; @@ -15,8 +15,9 @@ import com.seibel.lod.util.LodUtil; * if an array contain coordinate the order is the following * 0 for x, 1 for z in 2D * 0 for x, 1 for y, 2 for z in 3D + * + * */ - public class LodRegion { //x coord, @@ -82,7 +83,7 @@ public class LodRegion * This method can be used to insert data into the LodRegion * * @param dataPoint - * @return + * @return if the data was added successfully */ public boolean addData(byte detailLevel, int posX, int posZ, long[] dataPoint, boolean serverQuality) { @@ -90,14 +91,25 @@ public class LodRegion posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); if (!doesDataExist(detailLevel, posX, posZ) || serverQuality) { - - //update the number of node present + //update the number of nodes present //if (!doesDataExist(detailLevel, posX, posZ)) numberOfPoints++; - //add the node data - this.dataContainer[detailLevel].addData(dataPoint, posX, posZ); - return true; - } else + try + { + //add the node data + this.dataContainer[detailLevel].addData(dataPoint, posX, posZ); + return true; + } + catch (NullPointerException e) + { + String detailMessage = "pos: [" + posX + "," + posZ + "] dataPoint: [" + dataPoint + "] serverQuality: [" + serverQuality + "] dataContainer"; + detailMessage += this.dataContainer != null ? ": [NULL]" : " at detailLevel: [" + dataContainer[detailLevel] + "]"; + + ClientProxy.LOGGER.error("addSingleData: " + e.getMessage() + "\t" + detailMessage); + return false; + } + } + else { return false; } @@ -107,7 +119,7 @@ public class LodRegion * This method can be used to insert data into the LodRegion * * @param dataPoint - * @return + * @return if the data was added successfully */ public boolean addSingleData(byte detailLevel, int posX, int posZ, long dataPoint, boolean serverQuality) { @@ -115,14 +127,25 @@ public class LodRegion posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); if (!doesDataExist(detailLevel, posX, posZ) || serverQuality) { - - //update the number of node present + //update the number of nodes present //if (!doesDataExist(detailLevel, posX, posZ)) numberOfPoints++; - - //add the node data - this.dataContainer[detailLevel].addSingleData(dataPoint, posX, posZ); - return true; - } else + + try + { + //add the node data + this.dataContainer[detailLevel].addSingleData(dataPoint, posX, posZ); + return true; + } + catch (NullPointerException e) + { + String detailMessage = "pos: [" + posX + "," + posZ + "] dataPoint: [" + dataPoint + "] serverQuality: [" + serverQuality + "] dataContainer"; + detailMessage += this.dataContainer != null ? ": [NULL]" : " at detailLevel: [" + dataContainer[detailLevel] + "]"; + + ClientProxy.LOGGER.error("addSingleData: " + e.getMessage() + "\t" + detailMessage); + return false; + } + } + else { return false; } diff --git a/src/main/java/com/seibel/lod/objects/PosToRenderContainer.java b/src/main/java/com/seibel/lod/objects/PosToRenderContainer.java index 2ec972613..bff00e7a2 100644 --- a/src/main/java/com/seibel/lod/objects/PosToRenderContainer.java +++ b/src/main/java/com/seibel/lod/objects/PosToRenderContainer.java @@ -1,8 +1,14 @@ package com.seibel.lod.objects; +import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.util.LevelPosUtil; import com.seibel.lod.util.LodUtil; +/** + * + * @author Leonardo Amato + * @version 9-18-2021 + */ public class PosToRenderContainer { public byte minDetail; @@ -26,6 +32,18 @@ public class PosToRenderContainer public void addPosToRender(byte detailLevel, int posX, int posZ) { + // When rapidly changing dimensions the bufferBuidler can cause this, + // James isn't sure why, but this will prevent an exception at + // the very least (while stilling logging the problem). + if (numberOfPosToRender >= posToRender.length) + { + // This is might be due to dimensions having a different width + // when first loading in + ClientProxy.LOGGER.error("Unable to addPosToRender. numberOfPosToRender [" + numberOfPosToRender +"] detailLevel [" + detailLevel + "] Pos [" + posX + "," + posZ + "]"); + numberOfPosToRender++; // incrementing so we can see how many pos over the limit we would go + return; + } + //if(numberOfPosToRender >= posToRender.length) // posToRender = Arrays.copyOf(posToRender, posToRender.length*2); posToRender[numberOfPosToRender][0] = detailLevel; diff --git a/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java b/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java index 6c91f1dcc..6e88cd484 100644 --- a/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java +++ b/src/main/java/com/seibel/lod/objects/VerticalLevelContainer.java @@ -110,7 +110,7 @@ public class VerticalLevelContainer implements LevelContainer dataToMerge[2*z + x] = lowerLevelContainer.getData(childPosX, childPosZ); } } - data = DataPointUtil.mergeVerticalData(dataToMerge); + data = DataPointUtil.mergeMultiData(dataToMerge); addData(data,posX,posZ); } diff --git a/src/main/java/com/seibel/lod/proxy/GlProxy.java b/src/main/java/com/seibel/lod/proxy/GlProxy.java index c87457d3e..71f928495 100644 --- a/src/main/java/com/seibel/lod/proxy/GlProxy.java +++ b/src/main/java/com/seibel/lod/proxy/GlProxy.java @@ -128,7 +128,7 @@ public class GlProxy } if (!WGL.wglMakeCurrent(deviceContext, contextPointer)) - throw new IllegalStateException("Unable to change OpenGL contexts! tried to change to [" + newContext.toString() + "] from [" + currentContext.toString() + "] lod builder owner thread: " + (lodBuilderOwnerThread != null ? lodBuilderOwnerThread.getName() : "null")); + throw new IllegalStateException("Unable to change OpenGL contexts! tried to change to [" + newContext.toString() + "] from [" + currentContext.toString() + "] on thread: [" + Thread.currentThread().getName() + "] lod builder owner thread: " + (lodBuilderOwnerThread != null ? lodBuilderOwnerThread.getName() : "null")); if (newContext == GlProxyContext.LOD_BUILDER) lodBuilderOwnerThread = Thread.currentThread(); diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java index 03a7bd2fc..48c64e1c4 100644 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodRenderer.java @@ -23,7 +23,6 @@ import java.nio.FloatBuffer; import java.util.HashSet; import java.util.Iterator; -import net.minecraft.client.renderer.texture.NativeImage; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15C; @@ -53,6 +52,7 @@ import com.seibel.lod.wrappers.MinecraftWrapper; import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.VertexBuffer; import net.minecraft.client.renderer.vertex.VertexFormat; @@ -62,7 +62,6 @@ 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.MathHelper; import net.minecraft.util.math.vector.Matrix4f; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3f; @@ -73,7 +72,7 @@ import net.minecraft.util.math.vector.Vector3f; * This is where LODs are draw to the world. * * @author James Seibel - * @version 9-14-2021 + * @version 9-18-2021 */ public class LodRenderer { @@ -124,7 +123,10 @@ public class LodRenderer * This is used to determine if the LODs should be regenerated */ private int[] previousPos = new int[]{0,0,0}; + public NativeImage lightMap = null; + + // these variables are used to determine if the buffers should be rebuilt private long prevDayTime = 0; private double prevBrightness = 0; private int prevRenderDistance = 0; @@ -245,15 +247,20 @@ public class LodRenderer // get the default projection matrix so we can // reset it after drawing the LODs - float[] defaultProjMatrix = new float[16]; - GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, defaultProjMatrix); - + float[] mcProjMatrixRaw = new float[16]; + GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, mcProjMatrixRaw); + Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw); + // OpenGl outputs their matricies in col,row form instead of row,col + // (or maybe vice versa I have no idea :P) + mcProjectionMatrix.transpose(); + Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks); // required for setupFog and setupProjectionMatrix farPlaneBlockDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH; - setupProjectionMatrix(partialTicks); + setupProjectionMatrix(mcProjectionMatrix, partialTicks); + // commented out until we can add shaders to handle lighting //setupLighting(lodDim, partialTicks); NearFarFogSettings fogSettings = determineFogSettings(); @@ -278,6 +285,7 @@ public class LodRenderer Vector3d cameraDir = cameraEntity.getLookAngle().normalize(); cameraDir = mc.getOptions().getCameraType().isMirrored() ? cameraDir.reverse() : cameraDir; + boolean cullingDisabled = LodConfig.CLIENT.graphics.disableDirectionalCulling.get(); // used to determine what type of fog to render int halfWidth = vbos.length / 2; @@ -288,7 +296,7 @@ public class LodRenderer for (int j = 0; j < vbos.length; j++) { RegionPos vboPos = new RegionPos(i + lodDim.getCenterX() - lodDim.getWidth() / 2, j + lodDim.getCenterZ() - lodDim.getWidth() / 2); - if (RenderUtil.isRegionInViewFrustum(cameraEntity.blockPosition(), cameraDir, vboPos.blockPos())) + if (cullingDisabled || RenderUtil.isRegionInViewFrustum(cameraEntity.blockPosition(), cameraDir, vboPos.blockPos())) { if ((i > halfWidth - quarterWidth && i < halfWidth + quarterWidth) && (j > halfWidth - quarterWidth && j < halfWidth + quarterWidth)) setupFog(fogSettings.near.distance, fogSettings.near.quality); @@ -323,10 +331,8 @@ public class LodRenderer // reset the projection matrix so anything drawn after // the LODs will use the correct projection matrix - Matrix4f mvm = new Matrix4f(defaultProjMatrix); - mvm.transpose(); - gameRender.resetProjectionMatrix(mvm); - + gameRender.resetProjectionMatrix(mcProjectionMatrix); + // clear the depth buffer so anything drawn is drawn // over the LODs GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT); @@ -495,69 +501,50 @@ public class LodRenderer /** * create a new projection matrix and send it over to the GPU - *

- * A lot of this code is copied from renderLevel (line 567) - * in the GameRender class. The code copied is anything with - * a matrixStack and is responsible for making sure the LOD - * objects distort correctly relative to the rest of the world. - * Distortions are caused by: standing in a nether portal, - * nausea potion effect, walking bobbing. - * + * + * @param currentProjectionMatrix this is Minecraft's current projection matrix * @param partialTicks how many ticks into the frame we are */ - private void setupProjectionMatrix(float partialTicks) + private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float partialTicks) { - // Note: if the LOD objects don't distort correctly - // compared to regular minecraft terrain, make sure - // all the transformations in renderWorld are here too - - MatrixStack matrixStack = new MatrixStack(); - matrixStack.pushPose(); - - gameRender.bobHurt(matrixStack, partialTicks); - if (this.mc.getOptions().bobView) - { - gameRender.bobView(matrixStack, partialTicks); - } - - // potion and nausea effects - float f = MathHelper.lerp(partialTicks, this.mc.getPlayer().oPortalTime, this.mc.getPlayer().portalTime) * this.mc.getOptions().screenEffectScale * this.mc.getOptions().screenEffectScale; - if (f > 0.0F) - { - int i = this.mc.getPlayer().hasEffect(Effects.CONFUSION) ? 7 : 20; - float f1 = 5.0F / (f * f + 5.0F) - f * 0.04F; - f1 = f1 * f1; - Vector3f vector3f = new Vector3f(0.0F, MathHelper.SQRT_OF_TWO / 2.0F, MathHelper.SQRT_OF_TWO / 2.0F); - matrixStack.mulPose(vector3f.rotationDegrees((gameRender.tick + partialTicks) * i)); - matrixStack.scale(1.0F / f1, 1.0F, 1.0F); - float f2 = -(gameRender.tick + partialTicks) * i; - matrixStack.mulPose(vector3f.rotationDegrees(f2)); - } - - - // this projection matrix allows us to see past the normal - // world render distance - Matrix4f projectionMatrix = - Matrix4f.perspective( - getFov(partialTicks, true), - (float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(), - // it is possible to see the near clip plane, but - // you have to be flying quickly in spectator mode through ungenerated - // terrain, so I don't think it is much of an issue. - mc.getRenderDistance()/2, - farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2); - - // add the screen space distortions - projectionMatrix.multiply(matrixStack.last().pose()); - gameRender.resetProjectionMatrix(projectionMatrix); - return; + // create the new projection matrix + Matrix4f lodPoj = + Matrix4f.perspective( + getFov(partialTicks, true), + (float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(), + mc.getRenderDistance()/2, + farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2 / 4); + + // get Minecraft's un-edited projection matrix + // (this is before it is zoomed, distorted, etc.) + Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, true); + // true here means use "use fov setting" (probably) + + + // this logic strips away the defaultMcProj matrix so we + // can get the distortionMatrix, which represents all + // transformations, zooming, distortions, etc. done + // to Minecraft's Projection matrix + Matrix4f defaultMcProjInv = defaultMcProj.copy(); + defaultMcProjInv.invert(); + + Matrix4f distortionMatrix = defaultMcProjInv.copy(); + distortionMatrix.multiply(currentProjectionMatrix); + + + // edit the lod projection to match Minecraft's + // (so the LODs line up with the real world) + lodPoj.multiply(distortionMatrix); + + // send the projection over to the GPU + gameRender.resetProjectionMatrix(lodPoj); } /** * setup the lighting to be used for the LODs */ - @SuppressWarnings("deprecation") + @SuppressWarnings({ "deprecation", "unused" }) private void setupLighting(LodDimension lodDimension, float partialTicks) { // Determine if the player has night vision @@ -782,7 +769,7 @@ public class LodRenderer */ private void determineIfLodsShouldRegenerate(LodDimension lodDim) { - short renderDistance = (short) mc.getRenderDistance(); + short chunkRenderDistance = (short) mc.getRenderDistance(); //=============// // full regens // @@ -799,7 +786,7 @@ public class LodRenderer prevFogDistance = LodConfig.CLIENT.graphics.fogDistance.get(); prevRenderDistance = mc.getRenderDistance(); //should use this when it's ready - vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2]; + vanillaRenderedChunks = new boolean[chunkRenderDistance*2+2][chunkRenderDistance*2+2]; } // did the user change the debug setting? @@ -822,7 +809,7 @@ public class LodRenderer fullRegen = true; previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk); //should use this when it's ready - vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2]; + vanillaRenderedChunks = new boolean[chunkRenderDistance*2+2][chunkRenderDistance*2+2]; } prevPlayerPosTime = newTime; } @@ -858,13 +845,13 @@ public class LodRenderer prevChunkTime = newTime; } - // check if there is any newly generated terrain to show - if (mc.getWorld().getDayTime() - prevDayTime > 1000 || mc.getOptions().gamma != prevBrightness || lightMap == null) + // check if the lighting has changed + if (mc.getClientWorld().getDayTime() - prevDayTime > 1000 || mc.getOptions().gamma != prevBrightness || lightMap == null) { fullRegen = true; lightMap = mc.getCurrentLightMap(); prevBrightness = mc.getOptions().gamma; - prevDayTime = mc.getWorld().getDayTime(); + prevDayTime = mc.getClientWorld().getDayTime(); } @@ -876,23 +863,18 @@ public class LodRenderer // determine which LODs should not be rendered close to the player HashSet chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayer().blockPosition()); - int chunkX; - int chunkZ; + int xIndex; + int zIndex; for (ChunkPos pos : chunkPosToSkip) { - chunkX = pos.x - mc.getPlayer().xChunk + renderDistance + 1; - chunkZ = pos.z - mc.getPlayer().zChunk + renderDistance + 1; - try + xIndex = (pos.x - mc.getPlayer().xChunk) + chunkRenderDistance + 1; + zIndex = (pos.z - mc.getPlayer().zChunk) + chunkRenderDistance + 1; + + if (!vanillaRenderedChunks[xIndex][zIndex]) { - if (!vanillaRenderedChunks[chunkX][chunkZ]) - { - vanillaRenderedChunks[chunkX][chunkZ] = true; - vanillaRenderedChunksChanged = true; - lodDim.setToRegen(pos.getRegionX(), pos.getRegionZ()); - } - }catch (Exception e){ - System.out.println(vanillaRenderedChunks.length); - e.printStackTrace(); + vanillaRenderedChunks[xIndex][zIndex] = true; + vanillaRenderedChunksChanged = true; + lodDim.setToRegen(pos.getRegionX(), pos.getRegionZ()); } } @@ -900,7 +882,7 @@ public class LodRenderer // if the player is high enough, draw all LODs if(chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256) { - vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2]; + vanillaRenderedChunks = new boolean[chunkRenderDistance*2+2][chunkRenderDistance*2+2]; vanillaRenderedChunksChanged = true; } } diff --git a/src/main/java/com/seibel/lod/util/DataPointUtil.java b/src/main/java/com/seibel/lod/util/DataPointUtil.java index 1ba284de0..cb3e38f64 100644 --- a/src/main/java/com/seibel/lod/util/DataPointUtil.java +++ b/src/main/java/com/seibel/lod/util/DataPointUtil.java @@ -1,12 +1,8 @@ package com.seibel.lod.util; import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.wrappers.MinecraftWrapper; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.texture.NativeImage; -import javax.xml.crypto.Data; -import java.lang.annotation.Native; +import net.minecraft.client.renderer.texture.NativeImage; public class DataPointUtil { @@ -171,8 +167,8 @@ public class DataPointUtil public static int getColor(long dataPoint) { - int color = getBlue(dataPoint) << BLUE_COLOR_SHIFT; - color += getRed(dataPoint) << BLUE_COLOR_SHIFT; + //int color = getBlue(dataPoint) << BLUE_COLOR_SHIFT; + //color += getRed(dataPoint) << BLUE_COLOR_SHIFT; return (int) (dataPoint >>> COLOR_SHIFT); } @@ -220,7 +216,6 @@ public class DataPointUtil public static long mergeSingleData(long[] dataToMerge) { int numberOfChildren = 0; - int numberOfVoidChildren = 0; int tempAlpha = 0; int tempRed = 0; @@ -281,7 +276,7 @@ public class DataPointUtil } } - public static long[] mergeVerticalData(long[][] dataToMerge) + public static long[] mergeMultiData(long[][] dataToMerge) { //new code short[] projection = ThreadMapUtil.getProjectionShort((WORLD_HEIGHT + 1 ) >>> 4); diff --git a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java index a98fc9ec5..552538694 100644 --- a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java +++ b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java @@ -160,9 +160,9 @@ public class DetailDistanceUtil public static byte getLodDrawDetail(int detail) { - if (detail < minGenDetail) + if (detail < minDrawDetail) { - return lodGenDetails[minGenDetail].detailLevel; + return lodGenDetails[minDrawDetail].detailLevel; } else { return lodGenDetails[detail].detailLevel; diff --git a/src/main/java/com/seibel/lod/util/LevelPosUtil.java b/src/main/java/com/seibel/lod/util/LevelPosUtil.java index af3bc7547..06b047557 100644 --- a/src/main/java/com/seibel/lod/util/LevelPosUtil.java +++ b/src/main/java/com/seibel/lod/util/LevelPosUtil.java @@ -1,7 +1,5 @@ package com.seibel.lod.util; -import com.seibel.lod.util.LodUtil; - public class LevelPosUtil { public static int[] convert(int[] levelPos, byte newDetailLevel) diff --git a/src/main/java/com/seibel/lod/util/LodUtil.java b/src/main/java/com/seibel/lod/util/LodUtil.java index 069c47fff..19144c242 100644 --- a/src/main/java/com/seibel/lod/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/util/LodUtil.java @@ -25,10 +25,9 @@ import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.RegionPos; import com.seibel.lod.wrappers.MinecraftWrapper; -import it.unimi.dsi.fastutil.objects.ObjectList; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.renderer.WorldRenderer; -import net.minecraft.client.renderer.WorldRenderer.LocalRenderInformationContainer; +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk; import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; @@ -375,12 +374,12 @@ public class LodUtil // go through every RenderInfo to get the compiled chunks WorldRenderer renderer = mc.getLevelRenderer(); - ObjectList chunks = renderer.renderChunks; - for (WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : chunks) + for (WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : renderer.renderChunks) { - if (!worldrenderer$localrenderinformationcontainer.chunk.getCompiledChunk().hasNoRenderableLayers()) + CompiledChunk compiledChunk = worldrenderer$localrenderinformationcontainer.chunk.getCompiledChunk(); + if (!compiledChunk.hasNoRenderableLayers()) { - // add the ChunkPos for every empty compiled chunk + // add the ChunkPos for every rendered chunk BlockPos bpos = worldrenderer$localrenderinformationcontainer.chunk.getOrigin(); loadedPos.add(new ChunkPos(bpos)); diff --git a/src/main/java/com/seibel/lod/util/ThreadMapUtil.java b/src/main/java/com/seibel/lod/util/ThreadMapUtil.java index 405e53d1e..44dc66db9 100644 --- a/src/main/java/com/seibel/lod/util/ThreadMapUtil.java +++ b/src/main/java/com/seibel/lod/util/ThreadMapUtil.java @@ -7,23 +7,23 @@ public class ThreadMapUtil { private static final int NUMBER_OF_DIRECTION = 4; - public static final ConcurrentMap threadSingleAddDataMap = new ConcurrentHashMap(); - public static final ConcurrentMap threadSingleGetDataMap = new ConcurrentHashMap(); - public static final ConcurrentMap threadSingleUpdateMap = new ConcurrentHashMap(); - public static final ConcurrentMap threadBuilderArrayMap = new ConcurrentHashMap(); - public static final ConcurrentMap threadBuilderVerticalArrayMap = new ConcurrentHashMap(); - public static final ConcurrentMap threadVerticalAddDataMap = new ConcurrentHashMap(); - public static final ConcurrentMap threadVerticalGetDataMap = new ConcurrentHashMap(); - public static final ConcurrentMap threadVerticalUpdateMap = new ConcurrentHashMap(); - public static final ConcurrentMap threadVerticalIndexesMap = new ConcurrentHashMap(); + public static final ConcurrentMap threadSingleAddDataMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap threadSingleGetDataMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap threadSingleUpdateMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap threadBuilderArrayMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap threadBuilderVerticalArrayMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap threadVerticalAddDataMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap threadVerticalGetDataMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap threadVerticalUpdateMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap threadVerticalIndexesMap = new ConcurrentHashMap<>(); - public static final ConcurrentMap threadAdjData = new ConcurrentHashMap(); + public static final ConcurrentMap threadAdjData = new ConcurrentHashMap<>(); - public static final ConcurrentMap projectionMap = new ConcurrentHashMap(); - public static final ConcurrentMap projectionShortMap = new ConcurrentHashMap(); - public static final ConcurrentMap heightAndDepthMap = new ConcurrentHashMap(); - public static final ConcurrentMap singleDataToMergeMap = new ConcurrentHashMap(); + public static final ConcurrentMap projectionMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap projectionShortMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap heightAndDepthMap = new ConcurrentHashMap<>(); + public static final ConcurrentMap singleDataToMergeMap = new ConcurrentHashMap<>(); public static long[] getSingleAddDataArray() diff --git a/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java b/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java index 2ac7ad7a8..20be2727d 100644 --- a/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java @@ -14,16 +14,14 @@ import net.minecraft.client.network.play.ClientPlayNetHandler; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.WorldRenderer; -import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.renderer.model.ModelManager; +import net.minecraft.client.renderer.texture.NativeImage; +import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; import net.minecraft.profiler.IProfiler; import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.util.Direction; import net.minecraft.world.DimensionType; -import net.minecraft.world.IWorld; -import net.minecraft.world.World; -import org.lwjgl.system.CallbackI; /** * A singleton that wraps the Minecraft class @@ -174,8 +172,8 @@ public class MinecraftWrapper { return mc.getModelManager(); } - - public World getWorld() + + public ClientWorld getClientWorld() { return mc.level; }