Implement LOD generation, temporary storage, and rendering

This commit is contained in:
James Seibel
2021-01-20 15:52:13 -06:00
parent d1eabb4821
commit ed7ca54e41
5 changed files with 245 additions and 83 deletions
@@ -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);
}
}
@@ -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
@@ -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];
}
}
@@ -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);
}
}
@@ -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);
}
}