From 3694dcba4cf2b3b89fad43e671f26048905ed6a1 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 12 Jun 2021 18:19:45 -0500 Subject: [PATCH] Add variable detail LODs --- .../com/seibel/lod/builders/LodBuilder.java | 217 ++++++---- .../lod/builders/LodChunkGenWorker.java | 4 +- .../lodTemplates/AbstractLodTemplate.java | 10 +- .../lodTemplates/CubicLodTemplate.java | 176 +------- .../java/com/seibel/lod/enums/LodDetail.java | 76 +++- .../lod/handlers/LodDimensionFileHandler.java | 13 +- .../java/com/seibel/lod/objects/LodChunk.java | 393 +++++++++++------- .../com/seibel/lod/objects/LodDataPoint.java | 82 ++++ .../com/seibel/lod/objects/LodRegion.java | 5 +- 9 files changed, 554 insertions(+), 422 deletions(-) create mode 100644 src/main/java/com/seibel/lod/objects/LodDataPoint.java diff --git a/src/main/java/com/seibel/lod/builders/LodBuilder.java b/src/main/java/com/seibel/lod/builders/LodBuilder.java index ef7119f07..bda1fc230 100644 --- a/src/main/java/com/seibel/lod/builders/LodBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBuilder.java @@ -5,7 +5,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import com.seibel.lod.enums.ColorDirection; +import com.seibel.lod.enums.LodDetail; import com.seibel.lod.objects.LodChunk; +import com.seibel.lod.objects.LodDataPoint; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.LodWorld; import com.seibel.lod.util.LodUtils; @@ -26,7 +28,7 @@ import net.minecraft.world.gen.Heightmap; * (specifically: Lod World, Dimension, Region, and Chunk objects) * * @author James Seibel - * @version 5-29-2021 + * @version 6-12-2021 */ public class LodBuilder { @@ -41,13 +43,6 @@ public class LodBuilder public static final int CHUNK_DATA_WIDTH = LodChunk.WIDTH; public static final int CHUNK_SECTION_HEIGHT = LodChunk.WIDTH; - /** - * This is how many blocks are - * required at a specific y-value - * to constitute a LOD point - */ - private final int LOD_BLOCK_REQ = 32; - // the max number of blocks per layer = 64 (8*8) public LodBuilder() @@ -67,7 +62,7 @@ public class LodBuilder // we are not in the same world anymore // don't add this LOD return; - + // don't try to create an LOD object // if for some reason we aren't @@ -128,33 +123,49 @@ public class LodBuilder throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); } - Color[] colors = new Color[ColorDirection.values().length]; + LodDetail detail = LodChunk.DETAIL; + LodDataPoint[][] dataPoints = new LodDataPoint[detail.lengthCount][detail.lengthCount]; - - short height = determineTopPoint(chunk.getSections()); - short depth = determineBottomPoint(chunk.getSections()); - - - // determine the average color for each direction - for(ColorDirection dir : ColorDirection.values()) + for(int i = 0; i < detail.lengthCount * detail.lengthCount; i++) { - colors[dir.value] = generateLodColorForDirection(chunk, dir); + int startX = detail.startX[i]; + int startZ = detail.startZ[i]; + int endX = detail.endX[i]; + int endZ = detail.endZ[i]; + + + Color[] colors = new Color[ColorDirection.values().length]; + + short height = determineTopPointForArea(chunk.getSections(), startX, startZ, endX, endZ); + short depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ); + + + // determine the average color for each direction + for(ColorDirection dir : ColorDirection.values()) + { + colors[dir.value] = generateLodColorForAreaInDirection(chunk, dir, startX, startZ, endX, endZ); + } + + // check to see if there are any invisible sides + for(ColorDirection dir : ColorDirection.values()) + { + // if there are any invisible sides + // replace them with the top side + // (this is done to make sure oceans and other totally + // flat locations have the correct side colors) + if (dir == ColorDirection.BOTTOM || dir == ColorDirection.TOP) + continue; + if (colors[dir.value] == INVISIBLE) + colors[dir.value] = colors[ColorDirection.TOP.value]; + } + + int x = i / detail.lengthCount; + int z = i % detail.lengthCount; + + dataPoints[x][z] = new LodDataPoint(height, depth, colors); } - // check to see if there are any invisible sides - for(ColorDirection dir : ColorDirection.values()) - { - // if there are any invisible sides - // replace them with the top side - // (this is done to make sure oceans and other totally - // flat locations have the correct side colors) - if (dir == ColorDirection.BOTTOM || dir == ColorDirection.TOP) - continue; - if (colors[dir.value] == INVISIBLE) - colors[dir.value] = colors[ColorDirection.TOP.value]; - } - - return new LodChunk(chunk.getPos(), height, depth, colors); + return new LodChunk(chunk.getPos(), dataPoints); } @@ -169,21 +180,42 @@ public class LodBuilder /** * Find the lowest valid point from the bottom. + * @param chunkSections + * @param startX + * @param startZ + * @param endX + * @param endZ */ - private short determineBottomPoint(ChunkSection[] chunkSections) + private short determineBottomPointForArea(ChunkSection[] chunkSections, + int startX, int startZ, int endX, int endZ) { + int numberOfBlocksRequired = ((endX-startX) * (endZ-startZ) / 2); + // search from the bottom up - for(int i = 0; i < CHUNK_DATA_WIDTH; i++) + for(int section = 0; section < CHUNK_DATA_WIDTH; section++) { for(int y = 0; y < CHUNK_SECTION_HEIGHT; y++) { - if(isLayerValidLodPoint(chunkSections, i, y)) + int numberOfBlocksFound = 0; + + for(int x = startX; x < endX; x++) { - // we found - // enough blocks in this - // layer to count as an - // LOD point - return (short) (y + (i * CHUNK_SECTION_HEIGHT)); + for(int z = startZ; z < endZ; z++) + { + if(isLayerValidLodPoint(chunkSections, section, y, x, z)) + { + numberOfBlocksFound++; + + if (numberOfBlocksFound >= numberOfBlocksRequired) + { + // we found + // enough blocks in this + // layer to count as an + // LOD point + return (short) (y + (section * CHUNK_SECTION_HEIGHT)); + } + } + } } } } @@ -207,21 +239,42 @@ public class LodBuilder /** * Find the highest valid point from the Top + * @param chunkSections + * @param startX + * @param startZ + * @param endX + * @param endZ */ - private short determineTopPoint(ChunkSection[] chunkSections) + private short determineTopPointForArea(ChunkSection[] chunkSections, + int startX, int startZ, int endX, int endZ) { + int numberOfBlocksRequired = ((endX-startX) * (endZ-startZ) / 2); + // search from the top down for(int section = chunkSections.length - 1; section >= 0; section--) { for(int y = CHUNK_DATA_WIDTH - 1; y >= 0; y--) { - if(isLayerValidLodPoint(chunkSections, section, y)) + int numberOfBlocksFound = 0; + + for(int x = startX; x < endX; x++) { - // we found - // enough blocks in this - // layer to count as an - // LOD point - return (short) (y + (section * CHUNK_SECTION_HEIGHT)); + for(int z = startZ; z < endZ; z++) + { + if(isLayerValidLodPoint(chunkSections, section, y, x, z)) + { + numberOfBlocksFound++; + + if (numberOfBlocksFound >= numberOfBlocksRequired) + { + // we found + // enough blocks in this + // layer to count as an + // LOD point + return (short) (y + (section * CHUNK_SECTION_HEIGHT)); + } + } + } } } } @@ -253,7 +306,7 @@ public class LodBuilder /** * Generate the color for the given chunk in the given ColorDirection. */ - private Color generateLodColorForDirection(IChunk chunk, ColorDirection colorDir) + private Color generateLodColorForAreaInDirection(IChunk chunk, ColorDirection colorDir, int startX, int startZ, int endX, int endZ) { Minecraft mc = Minecraft.getInstance(); BlockColors bc = mc.getBlockColors(); @@ -261,19 +314,20 @@ public class LodBuilder switch (colorDir) { case TOP: - return generateLodColorVertical(chunk, colorDir, bc); + return generateLodColorVerticalOverArea(chunk, colorDir, bc, startX, startZ, endX, endZ); case BOTTOM: - return generateLodColorVertical(chunk, colorDir, bc); + return generateLodColorVerticalOverArea(chunk, colorDir, bc, startX, startZ, endX, endZ); + // TODO case NORTH: - return generateLodColorHorizontal(chunk, colorDir, bc); + return generateLodColorHorizontalOverArea(chunk, colorDir, bc); case SOUTH: - return generateLodColorHorizontal(chunk, colorDir, bc); + return generateLodColorHorizontalOverArea(chunk, colorDir, bc); case EAST: - return generateLodColorHorizontal(chunk, colorDir, bc); + return generateLodColorHorizontalOverArea(chunk, colorDir, bc); case WEST: - return generateLodColorHorizontal(chunk, colorDir, bc); + return generateLodColorHorizontalOverArea(chunk, colorDir, bc); } return INVISIBLE; @@ -290,39 +344,23 @@ public class LodBuilder */ private boolean isLayerValidLodPoint( ChunkSection[] chunkSections, - int sectionIndex, int y) + int sectionIndex, int y, + int x, int z) { - // search through this layer - int layerBlocks = 0; - - for(int x = 0; x < LodChunk.WIDTH; x++) + if(chunkSections[sectionIndex] == null) { - for(int z = 0; z < LodChunk.WIDTH; z++) + // this section doesn't have any blocks, + // it is not a valid section + return false; + } + else + { + if(chunkSections[sectionIndex].getBlockState(x, y, z) != null && + chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR) { - if(chunkSections[sectionIndex] == null) - { - // this section doesn't have any blocks, - // it is not a valid section - return false; - } - else - { - if(chunkSections[sectionIndex].getBlockState(x, y, z) != null && - chunkSections[sectionIndex].getBlockState(x, y, z).getBlock() != Blocks.AIR) - { - // we found a valid block in - // in this layer - layerBlocks++; - - if(layerBlocks >= LOD_BLOCK_REQ) - { - return true; - } - } - } - - } // z - } // x + return true; + } + } return false; } @@ -332,7 +370,9 @@ public class LodBuilder * * @throws IllegalArgumentException if given a ColorDirection other than TOP or BOTTOM */ - private Color generateLodColorVertical(IChunk chunk, ColorDirection colorDir, BlockColors bc) + private Color generateLodColorVerticalOverArea( + IChunk chunk, ColorDirection colorDir, BlockColors bc, + int startX, int startZ, int endX, int endZ) { if(colorDir != ColorDirection.TOP && colorDir != ColorDirection.BOTTOM) { @@ -362,9 +402,9 @@ public class LodBuilder int topMin = 0; int topIncrement = goTopDown? -1 : 1; - for(int x = 0; x < CHUNK_DATA_WIDTH; x++) + for(int x = startX; x < endX; x++) { - for(int z = 0; z < CHUNK_DATA_WIDTH; z++) + for(int z = startZ; z < endZ; z++) { boolean foundBlock = false; @@ -446,7 +486,8 @@ public class LodBuilder /** * Generates the color for the sides of the given chunk. */ - private Color generateLodColorHorizontal(IChunk chunk, ColorDirection colorDir, BlockColors bc) + private Color generateLodColorHorizontalOverArea( + IChunk chunk, ColorDirection colorDir, BlockColors bc) { if(colorDir != ColorDirection.NORTH && colorDir != ColorDirection.SOUTH && colorDir != ColorDirection.EAST && colorDir != ColorDirection.WEST) { diff --git a/src/main/java/com/seibel/lod/builders/LodChunkGenWorker.java b/src/main/java/com/seibel/lod/builders/LodChunkGenWorker.java index 89aecb8cf..edacf3118 100644 --- a/src/main/java/com/seibel/lod/builders/LodChunkGenWorker.java +++ b/src/main/java/com/seibel/lod/builders/LodChunkGenWorker.java @@ -16,7 +16,7 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker; * This is used to generate a LodChunk at a given ChunkPos. * * @author James Seibel - * @version 03-31-2021 + * @version 6-12-2021 */ public class LodChunkGenWorker implements IWorker { @@ -54,7 +54,7 @@ public class LodChunkGenWorker implements IWorker // be added to the current LodDimension if (lodDim.regionIsInRange(pos.x / LodRegion.SIZE, pos.z / LodRegion.SIZE)) { - IChunk chunk;// = serverWorld.getChunk(x, z, ChunkStatus.EMPTY, true); + IChunk chunk; //long startTime = System.currentTimeMillis(); chunk = serverWorld.getChunk(x, z, ChunkStatus.FEATURES); 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 c3a3225ba..80f1607aa 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/AbstractLodTemplate.java @@ -1,7 +1,5 @@ package com.seibel.lod.builders.lodTemplates; -import java.awt.Color; - import com.seibel.lod.objects.LodChunk; import com.seibel.lod.objects.LodDimension; @@ -12,16 +10,10 @@ import net.minecraft.client.renderer.BufferBuilder; * BufferBuilders. * * @author James Seibel - * @version 05-07-2021 + * @version 06-12-2021 */ public abstract class AbstractLodTemplate { - /** alpha used when drawing chunks in debug mode */ - protected int debugAlpha = 255; // 0 - 255 - protected Color debugBlack = new Color(0, 0, 0, debugAlpha); - protected Color debugWhite = new Color(255, 255, 255, debugAlpha); - - public abstract void addLodToBuffer(BufferBuilder buffer, LodDimension lodDim, LodChunk lod, double xOffset, double yOffset, double zOffset, 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 55a48f149..dbd56cd00 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java @@ -1,13 +1,11 @@ package com.seibel.lod.builders.lodTemplates; import java.awt.Color; -import java.util.EnumSet; import com.seibel.lod.enums.ColorDirection; -import com.seibel.lod.enums.RelativeChunkPos; +import com.seibel.lod.enums.LodDetail; import com.seibel.lod.objects.LodChunk; import com.seibel.lod.objects.LodDimension; -import com.seibel.lod.util.LodConfig; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.math.AxisAlignedBB; @@ -16,7 +14,7 @@ import net.minecraft.util.math.AxisAlignedBB; * Builds LODs as rectangular prisms. * * @author James Seibel - * @version 05-31-2021 + * @version 06-12-2021 */ public class CubicLodTemplate extends AbstractLodTemplate { @@ -37,118 +35,34 @@ public class CubicLodTemplate extends AbstractLodTemplate // Add this LOD to the BufferBuilder // using the quality setting set by the config - switch(LodConfig.CLIENT.lodDetail.get()) + LodDetail detail = LodChunk.DETAIL; //LodConfig.CLIENT.lodDetail.get(); + + int halfWidth = detail.width / 2; + + for(int i = 0; i < detail.lengthCount * detail.lengthCount; i++) { - // add a single LOD object for this chunk - case SINGLE: + int startX = detail.startX[i]; + int startZ = detail.startZ[i]; + int endX = detail.endX[i]; + int endZ = detail.endZ[i]; // returns null if the lod is empty at the given location - bbox = generateBoundingBox(centerLod.getHeight(), centerLod.getDepth(), LodChunk.WIDTH, xOffset, yOffset, zOffset); + bbox = generateBoundingBox( + centerLod.getAverageHeightOverArea(startX, startZ, endX, endZ), + centerLod.getAverageDepthOverArea(startX, startZ, endX, endZ), + detail.width, + xOffset - (halfWidth / 2) + detail.startX[i], + yOffset, + zOffset - (halfWidth / 2) + detail.startZ[i]); if (bbox != null) { - addBoundingBoxToBuffer(buffer, bbox, generateLodColors(centerLod, false)); + addBoundingBoxToBuffer(buffer, bbox, centerLod.getAverageColorOverArea(startX, startZ, endX, endZ, debugging)); } - - break; - - // add 4 LOD objects for this chunk - case DOUBLE: - /* - * This method generates LODs using the LodChunks that - * are adjacent to create an average quarter and thus - * smooth the transition between chunks. - */ - - // get the adjacent LodChunks - LodChunk[] lods = new LodChunk[RelativeChunkPos.values().length]; - for(RelativeChunkPos pos : RelativeChunkPos.values()) - lods[pos.index] = lodDim.getLodFromCoordinates(centerLod.x + pos.x, centerLod.z + pos.z); - - - int halfWidth = LodChunk.WIDTH / 2; - - // use the adjacent chunks to generate quarter sections - for(EnumSet set : RelativeChunkPos.CORNERS) - { - int x = 0; - int z = 0; - - // Weight the center LodChunk by this amount - // when taking the average. - // this should be between 3 and 6; - // if set to 1 (no extra weight) - // then the chunks don't appear to be averaged. - int centerWeight = 3; - - // how many LodChunks adjacent to the center - // are valid? - int validPoints = centerWeight; - - int avgHeight = centerLod.getHeight() * centerWeight; - int avgDepth = centerLod.getDepth() * centerWeight; - - int[][] colorAverages = new int[ColorDirection.values().length][3]; - Color[] colorToAdd = generateLodColors(centerLod, debugging); - for(int i = 0; i < centerWeight; i++) - colorAverages = addColorToColorAverages(colorAverages, colorToAdd); - - for(RelativeChunkPos cornerPos : set) - { - // set the x and y location based on which - // corner we are working on - if (RelativeChunkPos.DIAGONAL.contains(cornerPos)) - { - x = Math.min(cornerPos.x, 0) * halfWidth; - z = Math.min(cornerPos.z, 0) * halfWidth; - } - - LodChunk cornerLod = lods[cornerPos.index]; - if (cornerLod != null && !cornerLod.isLodEmpty()) - { - validPoints++; - - avgHeight += cornerLod.getHeight(); - avgDepth += cornerLod.getDepth(); - - // only generate average colors if we aren't debugging - // (this is to prevent everything from becoming grey) - if (!debugging) - colorToAdd = generateLodColors(cornerLod, debugging); - else - colorToAdd = generateLodColors(centerLod, debugging); - // add to the running color average - colorAverages = addColorToColorAverages(colorAverages, colorToAdd); - } - } - - - // convert the heights into actual averages - avgHeight /= validPoints; - avgDepth /= validPoints; - // calculate the average colors - Color[] colors = new Color[ColorDirection.values().length]; - for(ColorDirection dir : ColorDirection.values()) - { - for(int rgbIndex = 0; rgbIndex < 3; rgbIndex++) - colorAverages[dir.value][rgbIndex] /= validPoints; - colors[dir.value] = new Color(colorAverages[dir.value][0], colorAverages[dir.value][1], colorAverages[dir.value][2]); - } - - - // returns null if the lod is empty at the given location - bbox = generateBoundingBox(avgHeight, avgDepth, halfWidth, xOffset - (halfWidth / 2) + x + 12, yOffset, zOffset - (halfWidth / 2) + z + 12); - - if (bbox != null) - { - addBoundingBoxToBuffer(buffer, bbox, colors); - } - } - break; - } // case + } } - + /* private int[][] addColorToColorAverages(int[][] colorAverages, Color[] colorToAdd) { for(ColorDirection dir : ColorDirection.values()) @@ -167,7 +81,7 @@ public class CubicLodTemplate extends AbstractLodTemplate return colorAverages; } - + */ @@ -228,50 +142,4 @@ public class CubicLodTemplate extends AbstractLodTemplate - - - /** - * Determine the color for each side of this LOD. - */ - private Color[] generateLodColors(LodChunk lod, boolean debugging) - { - Color[] colors = new Color[ColorDirection.values().length]; - - if (!debugging) - { - // if NOT debugging, look to the config to determine - // how this LOD should be colored - switch (LodConfig.CLIENT.lodColorStyle.get()) - { - case TOP: - // only add the top's color to the array - for(ColorDirection dir : ColorDirection.values()) - colors[dir.value] = lod.getColor(ColorDirection.TOP); - break; - - case INDIVIDUAL_SIDES: - // add each direction's color to the array - for(ColorDirection dir : ColorDirection.values()) - colors[dir.value] = lod.getColor(dir); - break; - } - } - else - { - // if debugging draw the squares as a black and white checker board - if ((lod.x + lod.z) % 2 == 0) - for(ColorDirection dir : ColorDirection.values()) - // have each direction be the same - // color if debugging - colors[dir.value] = debugWhite; - else - for(ColorDirection dir : ColorDirection.values()) - colors[dir.value] = debugBlack; - } - - return colors; - } - - - } diff --git a/src/main/java/com/seibel/lod/enums/LodDetail.java b/src/main/java/com/seibel/lod/enums/LodDetail.java index 1849118f5..af4c40e93 100644 --- a/src/main/java/com/seibel/lod/enums/LodDetail.java +++ b/src/main/java/com/seibel/lod/enums/LodDetail.java @@ -1,10 +1,12 @@ package com.seibel.lod.enums; +import com.seibel.lod.objects.LodChunk; + /** - * single, quad + * single, double, quad, half, full * * @author James Seibel - * @version 05-29-2021 + * @version 06-12-2021 */ public enum LodDetail { @@ -12,14 +14,72 @@ public enum LodDetail SINGLE(1), /** render 4 LODs for each chunk */ - DOUBLE(2); + DOUBLE(2), - /** How many data points wide the related - * LodChunk object should contain */ - public final int value; + /** render 16 LODs for each chunk */ + QUAD(4), - private LodDetail(int newValue) + /** render 64 LODs for each chunk */ + HALF(8), + + /** render 256 LODs for each chunk */ + FULL(16); + + + /** How many LODs wide should + * be drawn per LodChunk */ + public final int lengthCount; + /** How wide each LOD is */ + public final int width; + + /* */ + public final int[] startX; + public final int[] startZ; + + public final int[] endX; + public final int[] endZ; + + + private LodDetail(int newLengthCount) { - value = newValue; + lengthCount = newLengthCount; + width = 16 / lengthCount; + + if(newLengthCount == LodChunk.WIDTH) + { + // this is to prevent overflow + newLengthCount = LodChunk.WIDTH - 1; + } + + startX = new int[lengthCount * lengthCount]; + endX = new int[lengthCount * lengthCount]; + + startZ = new int[lengthCount * lengthCount]; + endZ = new int[lengthCount * lengthCount]; + + + int index = 0; + for(int x = 0; x < newLengthCount; x++) + { + for(int z = 0; z < newLengthCount; z++) + { + startX[index] = x * width; + startZ[index] = z * width; + + // special case for FULL + if(width != 1) + { + endX[index] = (x*width) + width - 1; + endZ[index] = (z*width) + width - 1; + } + else + { + endX[index] = (x*width) + width; + endZ[index] = (z*width) + width; + } + + index++; + } + } } } diff --git a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java index 0fea95617..cd528e5b4 100644 --- a/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java +++ b/src/main/java/com/seibel/lod/handlers/LodDimensionFileHandler.java @@ -19,10 +19,14 @@ import com.seibel.lod.proxy.ClientProxy; * to file. * * @author James Seibel - * @version 05-31-2021 + * @version 6-12-2021 */ public class LodDimensionFileHandler { + /** This is what separates each piece of data */ + public static final char DATA_DELIMITER = ','; + + private LodDimension loadedDimension = null; public long regionLastWriteTime[][]; @@ -74,6 +78,9 @@ public class LodDimensionFileHandler */ public LodRegion loadRegionFromFile(int regionX, int regionZ) { + // TODO + return null; + /* String fileName = getFileNameAndPathForRegion(regionX, regionZ); File f = new File(fileName); @@ -174,6 +181,7 @@ public class LodDimensionFileHandler } return region; + */ } @@ -192,7 +200,8 @@ public class LodDimensionFileHandler */ public void saveDirtyRegionsToFileAsync() { - fileWritingThreadPool.execute(saveDirtyRegionsThread); + // TODO + //fileWritingThreadPool.execute(saveDirtyRegionsThread); } private Thread saveDirtyRegionsThread = new Thread(() -> diff --git a/src/main/java/com/seibel/lod/objects/LodChunk.java b/src/main/java/com/seibel/lod/objects/LodChunk.java index 7c3780569..e4bb7d18b 100644 --- a/src/main/java/com/seibel/lod/objects/LodChunk.java +++ b/src/main/java/com/seibel/lod/objects/LodChunk.java @@ -3,6 +3,9 @@ package com.seibel.lod.objects; import java.awt.Color; import com.seibel.lod.enums.ColorDirection; +import com.seibel.lod.enums.LodDetail; +import com.seibel.lod.handlers.LodDimensionFileHandler; +import com.seibel.lod.util.LodConfig; import net.minecraft.util.math.ChunkPos; @@ -11,7 +14,7 @@ import net.minecraft.util.math.ChunkPos; * and color data for an LOD object. * * @author James Seibel - * @version 05-29-2021 + * @version 6-12-2021 */ public class LodChunk { @@ -19,39 +22,29 @@ public class LodChunk private static final int DATA_DELIMITER_COUNT = 22; /** This is what separates each piece of data in the toData method */ - public static final char DATA_DELIMITER = ','; + private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER; /** Width of a Minecraft Chunk */ public static final int WIDTH = 16; + /** alpha used when drawing chunks in debug mode */ + private static final int DEBUG_ALPHA = 255; // 0 - 255 + private static final Color DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA); + private static final Color DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA); private static final Color INVISIBLE = new Color(0,0,0,0); + public static final LodDetail DETAIL = LodDetail.QUAD; + /** The x coordinate of the chunk. */ public int x = 0; /** The z coordinate of the chunk. */ public int z = 0; - /* - * The reason we are only using 1 height and depth point - * instead of multiple is because: - * 1. more points would drastically increase the amount of - * memory and disk space needed - * 2. more height/depth points require more color points, - * which can get out of hand quickly - * 3. with the increased disk space reading/writing would - * take far too long - * 4. the increased resolution is generally not worth it, - * 4 LODs per chunk is the limit of diminishing returns. - * And some of that could potentially be faked through - * smart LodTemplates - */ - private short height; - private short depth; - /** The average color for the 6 cardinal directions */ - public Color colors[]; + /** This stores the height and color for each data point in the LodChunk */ + public LodDataPoint dataPoints[][]; /** If true then this LodChunk contains no data */ private boolean empty = false; @@ -67,16 +60,11 @@ public class LodChunk x = 0; z = 0; - height = 0; - depth = 0; - colors = new Color[ColorDirection.values().length]; - - // by default have the colors invisible - for(ColorDirection dir : ColorDirection.values()) - colors[dir.value] = INVISIBLE; + dataPoints = new LodDataPoint[DETAIL.lengthCount][DETAIL.lengthCount]; } + // TODO /** * Creates an LodChunk from the string * generated by the toData method. @@ -84,107 +72,107 @@ public class LodChunk * @throws IllegalArgumentException if the data isn't valid to create a LodChunk * @throws NumberFormatException if any piece of data can't be converted at any point */ - public LodChunk(String data) throws IllegalArgumentException, NumberFormatException - { - /* - * data format: - * x, z, height, depth, rgb color data - * - * example: - * 5,8, 4, 0, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, - */ - - // make sure there are the correct number of entries - // in the data string (28) - int count = 0; - - for(int i = 0; i < data.length(); i++) - if(data.charAt(i) == DATA_DELIMITER) - count++; - - if(count != DATA_DELIMITER_COUNT) - throw new IllegalArgumentException("LodChunk constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + DATA_DELIMITER_COUNT + "."); - - - - // index we will use when going through the String - int index = 0; - int lastIndex = 0; - - - - // x and z position - index = data.indexOf(DATA_DELIMITER, 0); - x = Integer.parseInt(data.substring(0,index)); - - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - z = Integer.parseInt(data.substring(lastIndex+1,index)); - - // height - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - height = Short.parseShort(data.substring(lastIndex+1,index)); - - // depth - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); - depth = Short.parseShort(data.substring(lastIndex+1,index)); - - - - // color - colors = new Color[6]; - for(ColorDirection dir : ColorDirection.values()) - { - int red = 0; - int green = 0; - int blue = 0; - - // get r,g,b - for(int i = 0; i < 3; i++) - { - lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex + 1); - - String raw = ""; - switch(i) - { - case 0: - raw = data.substring(lastIndex+1,index); - red = Short.parseShort(raw); - break; - case 1: - raw = data.substring(lastIndex+1,index); - green = Short.parseShort(raw); - break; - case 2: - raw = data.substring(lastIndex+1,index); - blue = Short.parseShort(raw); - break; - } - } - - colors[dir.value] = new Color(red, green, blue); - } - - - // after going through all this - // is this LOD empty? - empty = determineIfEmtpy(); - } +// public LodChunk(String data) throws IllegalArgumentException, NumberFormatException +// { +// /* +// * data format: +// * x, z, height, depth, rgb color data +// * +// * example: +// * 5,8, 4, 0, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, +// */ +// +// // make sure there are the correct number of entries +// // in the data string (28) +// int count = 0; +// +// for(int i = 0; i < data.length(); i++) +// if(data.charAt(i) == DATA_DELIMITER) +// count++; +// +// if(count != DATA_DELIMITER_COUNT) +// throw new IllegalArgumentException("LodChunk constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + DATA_DELIMITER_COUNT + "."); +// +// +// +// // index we will use when going through the String +// int index = 0; +// int lastIndex = 0; +// +// +// +// // x and z position +// index = data.indexOf(DATA_DELIMITER, 0); +// x = Integer.parseInt(data.substring(0,index)); +// +// lastIndex = index; +// index = data.indexOf(DATA_DELIMITER, lastIndex+1); +// z = Integer.parseInt(data.substring(lastIndex+1,index)); +// +// // height +// lastIndex = index; +// index = data.indexOf(DATA_DELIMITER, lastIndex+1); +// // TODO +// //height = Short.parseShort(data.substring(lastIndex+1,index)); +// +// // depth +// lastIndex = index; +// index = data.indexOf(DATA_DELIMITER, lastIndex+1); +// //depth = Short.parseShort(data.substring(lastIndex+1,index)); +// +// +// +// // color +// //colors = new Color[6]; +// for(ColorDirection dir : ColorDirection.values()) +// { +// int red = 0; +// int green = 0; +// int blue = 0; +// +// // get r,g,b +// for(int i = 0; i < 3; i++) +// { +// lastIndex = index; +// index = data.indexOf(DATA_DELIMITER, lastIndex + 1); +// +// String raw = ""; +// switch(i) +// { +// case 0: +// raw = data.substring(lastIndex+1,index); +// red = Short.parseShort(raw); +// break; +// case 1: +// raw = data.substring(lastIndex+1,index); +// green = Short.parseShort(raw); +// break; +// case 2: +// raw = data.substring(lastIndex+1,index); +// blue = Short.parseShort(raw); +// break; +// } +// } +// +// // TODO +// //colors[dir.value] = new Color(red, green, blue); +// } +// +// +// // after going through all this +// // is this LOD empty? +// empty = determineIfEmtpy(); +// } /** * Create a LodChunk from the given values. */ - public LodChunk(ChunkPos pos, short newHeight, short newDepth, Color[] newColors) + public LodChunk(ChunkPos pos, LodDataPoint[][] newDataPoints) { x = pos.x; z = pos.z; - height = newHeight; - depth = newDepth; - colors = newColors; + dataPoints = newDataPoints; empty = determineIfEmtpy(); } @@ -217,27 +205,30 @@ public class LodChunk */ private boolean determineIfEmtpy() { - // we don't check the depth since the - // height should always be greater than or equal - // to the depth - if(height >= 0) + for(LodDataPoint[] dataPointArray : dataPoints) { - // the height is valid, - // do we have at least 1 non-invisible color? - - for(ColorDirection dir : ColorDirection.values()) - if(!colors[dir.value].equals(INVISIBLE)) - // at least one direction has a non-invisible color - return false; - - return true; - } - else - { - // the height is negative, - // this LodChunk is empty - return true; + for(LodDataPoint dataPoint : dataPointArray) + { + if (dataPoint == null) + continue; + + // we don't check the depth since the + // height should always be greater than or equal + // to the depth + if(dataPoint.height >= 0) + { + // the height is valid, + // do we have at least 1 non-invisible color? + for(ColorDirection dir : ColorDirection.values()) + if(!dataPoint.colors[dir.value].equals(INVISIBLE)) + // at least one direction has a non-invisible color + return false; + } + } } + + // we checked everywhere, this LodChunk is empty + return true; } @@ -247,28 +238,124 @@ public class LodChunk // output // //========// + /** + * Returns the data point for the given relative block position. + */ + public LodDataPoint getDataPointForBlockPos(int blockX, int blockZ) + { + if (blockX < 0 || blockX > WIDTH || blockX < 0 || blockZ > WIDTH) + throw new IllegalArgumentException("The coordinates given are outside the LodChunk"); + + return dataPoints[blockX / DETAIL.width][blockZ / DETAIL.width]; + } + /** Returns the color for the given direction */ - public Color getColor(ColorDirection dir) + public Color getColorForBlockPos(int blockX, int blockZ, ColorDirection dir) { - return colors[dir.value]; + return getDataPointForBlockPos(blockX, blockZ).colors[dir.value]; } - public short getHeight() + public short getHeightForBlockPos(int blockX, int blockZ) { - return height; + return getDataPointForBlockPos(blockX, blockZ).height; } - public short getDepth() + public short getDepthForBlockPos(int blockX, int blockZ) { - return depth; + return getDataPointForBlockPos(blockX, blockZ).depth; } + /** + * @param startX + * @param startZ + * @param endX + * @param endZ + * @return + */ + public short getAverageHeightOverArea(int startX, int startZ, int endX, int endZ) + { + int average = 0; + + for(int x = startX; x < endX; x++) + for(int z = startZ; z < endZ; z++) + average += getHeightForBlockPos(x,z); + + return (short) (average / ((endX - startX) * (endZ - startZ))); + } + + /** + * @param startX + * @param startZ + * @param endX + * @param endZ + * @return + */ + public short getAverageDepthOverArea(int startX, int startZ, int endX, int endZ) + { + int average = 0; + + for(int x = startX; x < endX; x++) + for(int z = startZ; z < endZ; z++) + average += getDepthForBlockPos(x,z); + + return (short) (average / ((endX - startX) * (endZ - startZ))); + } + + + /** + * Determine the color for each side of this LOD. + */ + public Color[] getAverageColorOverArea(int startX, int startZ, int endX, int endZ, boolean debugging) + { + Color[] colors = new Color[ColorDirection.values().length]; + + for(int x = startX; x < endX; x++) + { + for(int z = startZ; z < endZ; z++) + { + if (!debugging) + { + // if NOT debugging, look to the config to determine + // how this LOD should be colored + switch (LodConfig.CLIENT.lodColorStyle.get()) + { + case TOP: + // only add the top's color to the array + for(ColorDirection dir : ColorDirection.values()) + colors[dir.value] = getColorForBlockPos(x,z, ColorDirection.TOP); + break; + + case INDIVIDUAL_SIDES: + // add each direction's color to the array + for(ColorDirection dir : ColorDirection.values()) + colors[dir.value] = getColorForBlockPos(x,z, dir); + break; + } + } + else + { + // if debugging draw the squares as a black and white checker board + if ((x + z) % 2 == 0) + for(ColorDirection dir : ColorDirection.values()) + // have each direction be the same + // color if debugging + colors[dir.value] = DEBUG_WHITE; + else + for(ColorDirection dir : ColorDirection.values()) + colors[dir.value] = DEBUG_BLACK; + } + } + } + + return colors; + } + /** - * Outputs all data in csv format + * Outputs all data in a csv format * with the given delimiter. *
* Exports data in the form: @@ -286,14 +373,9 @@ public class LodChunk s += Integer.toString(x) + DATA_DELIMITER + Integer.toString(z) + DATA_DELIMITER; - s += Short.toString(height) + DATA_DELIMITER; - - s += Short.toString(depth) + DATA_DELIMITER; - - for(int i = 0; i < colors.length; i++) - { - s += Integer.toString(colors[i].getRed()) + DATA_DELIMITER + Integer.toString(colors[i].getGreen()) + DATA_DELIMITER + Integer.toString(colors[i].getBlue()) + DATA_DELIMITER; - } + for (LodDataPoint[] dataPointArray : dataPoints) + for(LodDataPoint dataPoint : dataPointArray) + s += dataPoint.toData(); return s; } @@ -306,7 +388,8 @@ public class LodChunk s += "x: " + x + " z: " + z + "\t"; - s += "(" + colors[ColorDirection.TOP.value].getRed() + ", " + colors[ColorDirection.TOP.value].getGreen() + ", " + colors[ColorDirection.TOP.value].getBlue() + ")"; + // TODO + //s += "(" + colors[ColorDirection.TOP.value].getRed() + ", " + colors[ColorDirection.TOP.value].getGreen() + ", " + colors[ColorDirection.TOP.value].getBlue() + ")"; return s; } diff --git a/src/main/java/com/seibel/lod/objects/LodDataPoint.java b/src/main/java/com/seibel/lod/objects/LodDataPoint.java new file mode 100644 index 000000000..2917c8f67 --- /dev/null +++ b/src/main/java/com/seibel/lod/objects/LodDataPoint.java @@ -0,0 +1,82 @@ +package com.seibel.lod.objects; + +import java.awt.Color; + +import com.seibel.lod.enums.ColorDirection; +import com.seibel.lod.handlers.LodDimensionFileHandler; + +/** + * This stores the height and color + * for a specific area in a LodChunk. + * + * @author James Seibel + * @version 6-12-2021 + */ +public class LodDataPoint +{ + /** This is what separates each piece of data in the toData method */ + private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER; + + private static final Color INVISIBLE = new Color(0,0,0,0); + + /** highest point */ + public short height; + + /** lowest point */ + public short depth; + + /** The average color for the 6 cardinal directions */ + public Color colors[]; + + + /** + * default constructor + */ + public LodDataPoint() + { + height = 0; + depth = 0; + colors = new Color[ColorDirection.values().length]; + + // by default have the colors invisible + for(ColorDirection dir : ColorDirection.values()) + colors[dir.value] = INVISIBLE; + } + + + public LodDataPoint(short newHeight, short newDepth, Color[] newColors) + { + height = newHeight; + depth = newDepth; + colors = newColors; + } + + + /** + * Outputs all data in a csv format + * with the given delimiter. + *
+ * Exports data in the form: + *
+ * height, depth, rgb color data + * + *
+ * example output: + *
+ * 4, 0, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, + */ + public String toData() + { + + String s = Short.toString(height) + DATA_DELIMITER; + + s += Short.toString(depth) + DATA_DELIMITER; + + for(int i = 0; i < colors.length; i++) + { + s += Integer.toString(colors[i].getRed()) + DATA_DELIMITER + Integer.toString(colors[i].getGreen()) + DATA_DELIMITER + Integer.toString(colors[i].getBlue()) + DATA_DELIMITER; + } + + return s; + } +} diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java index 52d5e768d..c96cd26c1 100644 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ b/src/main/java/com/seibel/lod/objects/LodRegion.java @@ -7,7 +7,7 @@ package com.seibel.lod.objects; * one file in the file system. * * @author James Seibel - * @version 1-22-2021 + * @version 6-12-2021 */ public class LodRegion { @@ -61,9 +61,6 @@ public class LodRegion int arrayX = Math.abs(chunkX % SIZE); int arrayZ = Math.abs(chunkZ % SIZE); - if(arrayX >= SIZE || arrayZ >= SIZE) - return null; - return chunks[arrayX][arrayZ]; }