From ed7ca54e41828ffe1b39042a4788eb665346a61a Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 20 Jan 2021 15:52:13 -0600 Subject: [PATCH] Implement LOD generation, temporary storage, and rendering --- .../backsun/lod/objects/LoadedRegions.java | 195 +++++++++++++++--- .../java/backsun/lod/objects/LodChunk.java | 17 +- .../java/backsun/lod/objects/LodRegion.java | 32 ++- .../java/backsun/lod/proxy/ClientProxy.java | 46 +++-- .../backsun/lod/renderer/LodRenderer.java | 38 ++-- 5 files changed, 245 insertions(+), 83 deletions(-) diff --git a/src/main/java/backsun/lod/objects/LoadedRegions.java b/src/main/java/backsun/lod/objects/LoadedRegions.java index 17bee100b..3170afdf7 100644 --- a/src/main/java/backsun/lod/objects/LoadedRegions.java +++ b/src/main/java/backsun/lod/objects/LoadedRegions.java @@ -7,13 +7,14 @@ import net.minecraft.world.DimensionType; * currently needed by the LodRenderer. * * @author James Seibel - * @version 10-25-2020 + * @version 1-20-2021 */ public class LoadedRegions { public final DimensionType dimension; - private int maxWidth; + private int width; // if this ever changes make sure to update the halfWidth too + private int halfWidth; public LodRegion regions[][]; @@ -23,28 +24,33 @@ public class LoadedRegions public LoadedRegions(DimensionType newDimension, int newMaxWidth) { dimension = newDimension; - maxWidth = newMaxWidth; + width = newMaxWidth; + + regions = new LodRegion[width][width]; + + centerX = 0; + centerZ = 0; + + halfWidth = (int)Math.floor(width / 2); } - /** - * Move over all data currently stored and update the centerX and Z - */ public void move(int xOffset, int zOffset) { // 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 - if (Math.abs(xOffset) >= maxWidth || Math.abs(zOffset) >= maxWidth) + if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width) { - for(int x = 0; x < maxWidth; x++) + for(int x = 0; x < width; x++) { - for(int z = 0; z < maxWidth; z++) + for(int z = 0; z < width; z++) { regions[x][z] = null; } } + // update the new center centerX += xOffset; centerZ += zOffset; @@ -54,56 +60,181 @@ public class LoadedRegions // X - - // if xOffset is positive cut off the left side - // (move the center to the right) - int start = (xOffset > 0)? 0 : maxWidth - 1; - int min = (xOffset > 0)? 0 : maxWidth - Math.abs(xOffset) + centerX; - int max = (xOffset > 0)? xOffset : maxWidth - 1 - xOffset; - int increment = (xOffset > 0)? 1 : -1; - - for(int x = start; x >= min && x < max; x += increment) + if(xOffset > 0) { - for(int z = 0; z < maxWidth; z++) + // move everything over to the left (as the center moves to the right) + for(int x = 0; x < width; x++) { - regions[Math.abs((x + centerX) % maxWidth)][Math.abs((z + centerZ) % maxWidth)] = null; + for(int z = 0; z < width; z++) + { + if(x + xOffset < width) + regions[x][z] = regions[x + xOffset][z]; + else + regions[x][z] = null; + } } } + else + { + // move everything over to the right (as the center moves to the left) + for(int x = width - 1; x >= 0; x--) + { + for(int z = 0; z < width; z++) + { + if(x + xOffset >= 0) + regions[x][z] = regions[x + xOffset][z]; + else + regions[x][z] = null; + } + } + } + // Z - start = (zOffset > 0)? 0 : maxWidth - 1; - min = (zOffset > 0)? 0 : maxWidth - Math.abs(zOffset) + centerZ; - max = (zOffset > 0)? zOffset : maxWidth - 1 - zOffset; - increment = (zOffset > 0)? 1 : -1; - - for(int x = 0; x < maxWidth; x++) + if(zOffset > 0) { - for(int z = start; z >= min && z < max; z += increment) + // move everything up (as the center moves down) + for(int x = 0; x < width; x++) { - regions[Math.abs((x + centerX) % maxWidth)][Math.abs((z + centerZ) % maxWidth)] = null; + for(int z = 0; z < width; z++) + { + if(z + zOffset < width) + regions[x][z] = regions[x][z + zOffset]; + else + regions[x][z] = null; + } } } + else + { + // move everything down (as the center moves up) + for(int x = 0; x < width; x++) + { + for(int z = width - 1; z >= 0; z--) + { + if(z + zOffset >= 0) + regions[x][z] = regions[x][z + zOffset]; + else + regions[x][z] = null; + } + } + } + + // update the new center centerX += xOffset; centerZ += zOffset; } + + public int getCenterX() + { + return centerX; + } + + public int getCenterZ() + { + return centerZ; + } + + + + + + + + public LodRegion getRegion(int regionX, int regionZ) + { + int xIndex = (regionX - centerX) + halfWidth; + int zIndex = (centerZ - regionZ) + halfWidth; + + if (xIndex < 0 || xIndex >= width || zIndex < 0 || zIndex >= width) + // out of range + return null; + + return regions[xIndex][zIndex]; + } + + /** + * Overwrite the LodRegion at the location of newRegion with newRegion. + */ + public void setRegion(LodRegion newRegion) + { + int xIndex = (newRegion.x - centerX) + halfWidth; + int zIndex = (centerZ - newRegion.z) + halfWidth; + + if (xIndex < 0 || xIndex >= width || zIndex < 0 || zIndex >= width) + // out of range + return; + + regions[xIndex][zIndex] = newRegion; + } + + + + + + + public void addLod(LodChunk lod) + { + int x = lod.x / 16; + int z = lod.z / 16; + + // prevent issues if X/Z is negative and less than 16 + if (lod.x < 0) + { + x = (Math.abs(x) * -1) - 1; + } + if (lod.z < 0) + { + z = (Math.abs(z) * -1) - 1; + } + + LodRegion region = getRegion(x, z); + + if (region == null) + { + // if no region exists, create it + region = new LodRegion(x, z); + setRegion(region); + } + + // TODO check what should be happening here + region.addLod(lod); + } + + /** * Returns null if the LodChunk isn't loaded */ public LodChunk getChunkFromCoordinates(int chunkX, int chunkZ) { - int xIndex = (chunkX + centerX) % maxWidth; - int zIndex = (chunkZ + centerZ) % maxWidth; + // (chunkX + centerX) % width + int xIndex = (chunkX + centerX) / LodRegion.SIZE; + int zIndex = (chunkZ + centerZ) / LodRegion.SIZE; - LodRegion region = regions[xIndex / maxWidth][zIndex / maxWidth]; + // prevent issues if chunkX/Z is negative and less than width + if (chunkX < 0) + { + xIndex = (Math.abs(xIndex) * -1) - 1; + } + if (chunkZ < 0) + { + zIndex = (Math.abs(zIndex) * -1) - 1; + } + + LodRegion region = getRegion(xIndex, zIndex); + + // TODO should abs be used here? + //if(chunkX < 0 || chunkZ < 0) + // return null; if(region == null) return null; - return region.chunks[xIndex % LodRegion.SIZE][xIndex % LodRegion.SIZE]; + return region.getLod(chunkX, chunkZ); } } diff --git a/src/main/java/backsun/lod/objects/LodChunk.java b/src/main/java/backsun/lod/objects/LodChunk.java index 585429ab6..54b0b0a94 100644 --- a/src/main/java/backsun/lod/objects/LodChunk.java +++ b/src/main/java/backsun/lod/objects/LodChunk.java @@ -16,7 +16,7 @@ import net.minecraft.world.chunk.storage.ExtendedBlockStorage; * and color data for an LOD object. * * @author James Seibel - * @version 10-17-2020 + * @version 1-20-2021 */ public class LodChunk { @@ -26,8 +26,10 @@ public class LodChunk /** This is what separates each piece of data in the toData method */ public static final char DATA_DELIMITER = ','; - private final int CHUNK_DATA_WIDTH = 16; - private final int CHUNK_DATA_HEIGHT = 16; + public static final int WIDTH = 16; + + private final int CHUNK_DATA_WIDTH = WIDTH; + private final int CHUNK_DATA_HEIGHT = WIDTH; /** * This is how many blocks are @@ -39,7 +41,6 @@ public class LodChunk // since each layer is 1/4 the chunk - /** The x coordinate of the chunk. */ public int x; /** The z coordinate of the chunk. */ @@ -142,7 +143,7 @@ public class LodChunk lastIndex = index; index = data.indexOf(DATA_DELIMITER, lastIndex); - top[loc.index] = Short.parseShort(data.substring(lastIndex,index - 1)); + top[loc.value] = Short.parseShort(data.substring(lastIndex,index - 1)); } @@ -152,7 +153,7 @@ public class LodChunk lastIndex = index; index = data.indexOf(DATA_DELIMITER, lastIndex); - bottom[loc.index] = Short.parseShort(data.substring(lastIndex,index - 1)); + bottom[loc.value] = Short.parseShort(data.substring(lastIndex,index - 1)); } @@ -214,8 +215,8 @@ public class LodChunk // generate the top and bottom points of this LOD for(LodLocation loc : LodLocation.values()) { - top[loc.index] = generateLodSection(chunk, true, loc); - bottom[loc.index] = generateLodSection(chunk, false, loc); + top[loc.value] = generateLodSection(chunk, true, loc); + bottom[loc.value] = generateLodSection(chunk, false, loc); } // determine the average color for each direction diff --git a/src/main/java/backsun/lod/objects/LodRegion.java b/src/main/java/backsun/lod/objects/LodRegion.java index 79d62aa3f..0af7431c7 100644 --- a/src/main/java/backsun/lod/objects/LodRegion.java +++ b/src/main/java/backsun/lod/objects/LodRegion.java @@ -7,10 +7,11 @@ package backsun.lod.objects; * one file in the file system. * * @author James Seibel - * @version 10-22-2020 + * @version 1-20-2021 */ public class LodRegion { + /** number of chunks wide */ public static final int SIZE = 32; /** X coordinate of this region */ @@ -18,7 +19,7 @@ public class LodRegion /** Z coordinate of this region */ public final int z; - public LodChunk chunks[][]; + private LodChunk chunks[][]; public LodRegion(int regionX, int regionZ) @@ -28,4 +29,31 @@ public class LodRegion chunks = new LodChunk[SIZE][SIZE]; } + + + public void addLod(LodChunk lod) + { + // we use ABS since LODs can be negative, but if they are + // the region will negative first, therefore we don't have to + // store the LOD chunks at negative indexes since we search + // LOD the region first + int xIndex = Math.abs(lod.x % LodChunk.WIDTH); + int zIndex = Math.abs(lod.z % LodChunk.WIDTH); + + chunks[xIndex][zIndex] = lod; + } + + + public LodChunk getLod(int x, int z) + { + // since we add LOD's with ABS, we get them the same way + x = Math.abs(x); + z = Math.abs(z); + + if(x >= SIZE || z >= SIZE) + return null; + + return chunks[x][z]; + } + } diff --git a/src/main/java/backsun/lod/proxy/ClientProxy.java b/src/main/java/backsun/lod/proxy/ClientProxy.java index 3faf90144..0b20d8f1f 100644 --- a/src/main/java/backsun/lod/proxy/ClientProxy.java +++ b/src/main/java/backsun/lod/proxy/ClientProxy.java @@ -1,5 +1,6 @@ package backsun.lod.proxy; +import backsun.lod.objects.LoadedRegions; import backsun.lod.objects.LodChunk; import backsun.lod.objects.LodRegion; import backsun.lod.renderer.LodRenderer; @@ -16,21 +17,19 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; * This is used by the client. * * @author James_Seibel - * @version 10-22-2020 + * @version 1-20-2021 */ public class ClientProxy extends CommonProxy { private LodRenderer renderer; private LodRegionFileHandler rfHandler; - //TODO have the ability to store multiple regions based on how large the user's view distance is - private LodRegion region; + private LoadedRegions regions; + + private int regionWidth = 5; public ClientProxy() { rfHandler = new LodRegionFileHandler(); - - - } @@ -43,6 +42,20 @@ public class ClientProxy extends CommonProxy @SubscribeEvent public void renderWorldLastEvent(RenderWorldLastEvent event) { + double playerX = Minecraft.getMinecraft().player.posX; + double playerZ = Minecraft.getMinecraft().player.posZ; + + // TODO make sure moving between regions works correctly + // move the regions based on the player's movement //TODO deal with -0 - -15 + int xOffset = ((int)playerX / (LodChunk.WIDTH * LodRegion.SIZE)) - regions.getCenterX(); + int zOffset = ((int)playerZ / (LodChunk.WIDTH * LodRegion.SIZE)) - regions.getCenterZ(); + + if (xOffset != 0 || zOffset != 0) + { + regions.move(xOffset, zOffset); + } + + // we wait to create the renderer until the first frame // to make sure that the EntityRenderer has // been created, that way we can get the fovModifer @@ -63,7 +76,7 @@ public class ClientProxy extends CommonProxy //===============// // TODO determine if a old region should be unloaded - // use the chunkUnloadedEvent + // use the chunkUnloadedEvent, or player moved? @SubscribeEvent public void chunkLoadEvent(ChunkEvent event) @@ -87,6 +100,8 @@ public class ClientProxy extends CommonProxy * Use this for generating chunks and maybe determining if they are loaded at all? + Could I create my own chunk generator and multithread it? It wouldn't save to the world, but could I save it for LODs? + chunk = Minecraft.getMinecraft().getIntegratedServer().getWorld(0).getChunkProvider().chunkGenerator.generateChunk(chunk.x, chunk.z); System.out.println(chunk.x + " " + chunk.z + "\tloaded: " + chunk.isLoaded() + "\tpop: " + chunk.isPopulated() + "\tter pop: " + chunk.isTerrainPopulated()); @@ -102,24 +117,23 @@ public class ClientProxy extends CommonProxy // or null chunks in this method) if (chunk != null && isValidChunk(chunk) && Minecraft.getMinecraft().world != null) { - LodChunk c = new LodChunk(chunk, Minecraft.getMinecraft().world); + LodChunk lod = new LodChunk(chunk, Minecraft.getMinecraft().world); - // TODO does this work with negative chunks? - // TODO set up dynamic/multiple regions - if (region == null || (region.x != (c.x / 32) && region.z != (c.z / 32))) + + if (regions == null) { - region = new LodRegion(c.x / 32, c.z / 32); + regions = new LoadedRegions(null, regionWidth); } - region.data[Math.abs(c.x % 32)][Math.abs(c.z % 32)] = c; + regions.addLod(lod); if (renderer != null) { - //TODO send data to renderer - renderer.renderRegions = region; + renderer.regions = regions; } - rfHandler.saveRegionToDisk(region); + // TODO + //rfHandler.saveRegionToDisk(regions); } } diff --git a/src/main/java/backsun/lod/renderer/LodRenderer.java b/src/main/java/backsun/lod/renderer/LodRenderer.java index ee7fad421..15714c8c6 100644 --- a/src/main/java/backsun/lod/renderer/LodRenderer.java +++ b/src/main/java/backsun/lod/renderer/LodRenderer.java @@ -9,6 +9,7 @@ import backsun.lod.objects.LoadedRegions; import backsun.lod.objects.LodChunk; import backsun.lod.util.OfConfig; import backsun.lod.util.enums.ColorDirection; +import backsun.lod.util.enums.LodLocation; import backsun.lod.util.fog.FogMode; import backsun.lod.util.fog.FogType; import net.minecraft.client.Minecraft; @@ -21,11 +22,11 @@ import net.minecraft.util.math.AxisAlignedBB; /** * @author James Seibel - * @version 10-25-2020 + * @version 1-20-2021 */ public class LodRenderer { - public boolean debugging = false; + public boolean debugging = true; private Minecraft mc; private float farPlaneDistance; @@ -34,12 +35,14 @@ public class LodRenderer public static final int LOD_WIDTH = 16; public static final int MINECRAFT_CHUNK_WIDTH = 16; + public int defaultLodHeight = 0; + private Tessellator tessellator; private BufferBuilder bufferBuilder; private OfConfig ofConfig; - public LoadedRegions regions; + public LoadedRegions regions = null; @@ -112,8 +115,6 @@ public class LodRenderer int totalLength = (int) farPlaneDistance * VIEW_DISTANCE_MULTIPLIER; int numbOfBoxesWide = (totalLength / LOD_WIDTH); - int lodHeight = 0; - // this where we will start drawing squares // (exactly half the total width) int startX = (-LOD_WIDTH * (numbOfBoxesWide / 2)) + playerXChunkOffset; @@ -126,13 +127,6 @@ public class LodRenderer Color colorArray[] = new Color[numbOfBoxesWide * numbOfBoxesWide]; - // used for debugging - // and drawing a checkerboard - boolean alternateColor = false; - boolean evenWidth = false; - if (debugging && numbOfBoxesWide % 2 == 0) - evenWidth = true; - @@ -142,13 +136,10 @@ public class LodRenderer mc.world.profiler.endStartSection("LOD generation"); + // x axis for (int i = 0; i < numbOfBoxesWide; i++) { - // this is so that the debug pattern will be a checkerboard instead of stripes - if (debugging && evenWidth) - alternateColor = !alternateColor; - // z axis for (int j = 0; j < numbOfBoxesWide; j++) { @@ -158,28 +149,27 @@ public class LodRenderer startX; // offset so the center LOD block is centered underneath the player double zOffset = -cameraZ + (LOD_WIDTH * j) + startZ; - int chunkX = ((LOD_WIDTH * j) + startX) / MINECRAFT_CHUNK_WIDTH; - int chunkZ = ((LOD_WIDTH * i) + startZ) / MINECRAFT_CHUNK_WIDTH; - + int chunkX = ((LOD_WIDTH * i) + startX) / MINECRAFT_CHUNK_WIDTH; + int chunkZ = ((LOD_WIDTH * j) + startZ) / MINECRAFT_CHUNK_WIDTH; LodChunk lod = regions.getChunkFromCoordinates(chunkX, chunkZ); if (lod == null) { colorArray[i + (j * numbOfBoxesWide)] = new Color(0,0,0,0); - lodArray[i + (j * numbOfBoxesWide)] = new AxisAlignedBB(0, lodHeight, 0, LOD_WIDTH, lodHeight, LOD_WIDTH); + lodArray[i + (j * numbOfBoxesWide)] = new AxisAlignedBB(0, defaultLodHeight, 0, LOD_WIDTH, defaultLodHeight, LOD_WIDTH); continue; } Color c = lod.colors[ColorDirection.TOP.index]; - double yOffset = -cameraY + lod.top[0]; + double yOffset = -cameraY; // if debugging draw the squares as a black and white checker board if (debugging) { - if (alternateColor) + if ((i + j) % 2 == 0) c = white; else c = black; @@ -188,8 +178,6 @@ public class LodRenderer c = red; colorArray[i + (j * numbOfBoxesWide)] = c; - - alternateColor = !alternateColor; } @@ -211,7 +199,7 @@ public class LodRenderer colorArray[i + (j * numbOfBoxesWide)] = c; // add the new box to the array - lodArray[i + (j * numbOfBoxesWide)] = new AxisAlignedBB(0, lodHeight, 0, LOD_WIDTH, lodHeight, LOD_WIDTH).offset(xOffset, yOffset, zOffset); + lodArray[i + (j * numbOfBoxesWide)] = new AxisAlignedBB(0, lod.bottom[LodLocation.NE.value], 0, LOD_WIDTH, lod.bottom[LodLocation.NE.value], LOD_WIDTH).offset(xOffset, yOffset, zOffset); } }