change the packages from com.backsun.lod... to com.seibel.lod...

This commit is contained in:
James Seibel
2021-05-31 19:30:48 -05:00
parent 884f6811a1
commit 84125735a1
34 changed files with 110 additions and 108 deletions
@@ -0,0 +1,313 @@
package com.seibel.lod.objects;
import java.awt.Color;
import com.seibel.lod.enums.ColorDirection;
import net.minecraft.util.math.ChunkPos;
/**
* This object contains position
* and color data for an LOD object.
*
* @author James Seibel
* @version 05-29-2021
*/
public class LodChunk
{
/** how many different pieces of data are in one line */
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 = ',';
/** Width of a Minecraft Chunk */
public static final int WIDTH = 16;
private static final Color INVISIBLE = new Color(0,0,0,0);
/** 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[];
/** If true then this LodChunk contains no data */
private boolean empty = false;
/**
* Create an empty invisible LodChunk at (0,0)
*/
public LodChunk()
{
empty = true;
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;
}
/**
* Creates an LodChunk from the string
* generated by the toData method.
*
* @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();
}
/**
* Create a LodChunk from the given values.
*/
public LodChunk(ChunkPos pos, short newHeight, short newDepth, Color[] newColors)
{
x = pos.x;
z = pos.z;
height = newHeight;
depth = newDepth;
colors = newColors;
empty = determineIfEmtpy();
}
//================//
// misc functions //
//================//
/**
* Returns true if this LodChunk is an emptyPlaceholder
*/
public boolean isPlaceholder()
{
return empty;
}
public boolean isLodEmpty()
{
return empty;
}
/**
* Returns true if this LOD is either invisible
* from every direction or doesn't have a valid height.
*/
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)
{
// 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;
}
}
//========//
// output //
//========//
/** Returns the color for the given direction */
public Color getColor(ColorDirection dir)
{
return colors[dir.value];
}
public short getHeight()
{
return height;
}
public short getDepth()
{
return depth;
}
/**
* Outputs all data in csv format
* with the given delimiter.
* <br>
* Exports data in the form:
* <br>
* x, z, height, depth, rgb color data
*
* <br>
* example output:
* <br>
* 5,8, 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 = "";
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;
}
return s;
}
@Override
public String toString()
{
String s = "";
s += "x: " + x + " z: " + z + "\t";
s += "(" + colors[ColorDirection.TOP.value].getRed() + ", " + colors[ColorDirection.TOP.value].getGreen() + ", " + colors[ColorDirection.TOP.value].getBlue() + ")";
return s;
}
}
@@ -0,0 +1,407 @@
package com.seibel.lod.objects;
import java.io.File;
import java.io.IOException;
import com.seibel.lod.handlers.LodDimensionFileHandler;
import com.seibel.lod.util.LodUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerWorld;
/**
* This object holds all loaded LOD regions
* for a given dimension.
*
* @author James Seibel
* @version 03-19-2021
*/
public class LodDimension
{
public final DimensionType dimension;
private volatile int width;
private volatile int halfWidth;
public LodRegion regions[][];
public boolean isRegionDirty[][];
private int centerX;
private int centerZ;
private LodDimensionFileHandler fileHandler;
public LodDimension(DimensionType newDimension, LodWorld lodWorld, int newMaxWidth)
{
dimension = newDimension;
width = newMaxWidth;
try
{
Minecraft mc = Minecraft.getInstance();
File saveDir;
if(mc.isIntegratedServerRunning())
{
// local world
ServerWorld serverWorld = LodUtils.getServerWorldFromDimension(newDimension);
// provider needs a separate variable to prevent
// the compiler from complaining
ServerChunkProvider provider = serverWorld.getChunkProvider();
saveDir = new File(provider.getSavedData().folder.getCanonicalFile() + "\\lod");
}
else
{
// connected to server
saveDir = new File(mc.gameDir.getCanonicalFile() +
"\\lod server data\\" + LodUtils.getDimensionIDFromWorld(mc.world));
}
fileHandler = new LodDimensionFileHandler(saveDir, this);
}
catch(IOException e)
{
// the file handler wasn't able to be created
// we won't be able to read or write any files
}
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
// populate isRegionDirty
for(int i = 0; i < width; i++)
for(int j = 0; j < width; j++)
isRegionDirty[i][j] = false;
centerX = 0;
centerZ = 0;
halfWidth = (int)Math.floor(width / 2);
}
/**
* Move the center of this LodDimension and move all owned
* regions over by the given x and z offset.
*/
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) >= width || Math.abs(zOffset) >= width)
{
for(int x = 0; x < width; x++)
{
for(int z = 0; z < width; z++)
{
regions[x][z] = null;
}
}
// update the new center
centerX += xOffset;
centerZ += zOffset;
return;
}
// X
if(xOffset > 0)
{
// move everything over to the left (as the center moves to the right)
for(int x = 0; x < width; x++)
{
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
if(zOffset > 0)
{
// move everything up (as the center moves down)
for(int x = 0; x < width; x++)
{
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;
}
/**
* Gets the region at the given X and Z
* <br>
* Returns null if the region doesn't exist
* or is outside the loaded area.
*/
public LodRegion getRegion(int regionX, int regionZ)
{
int xIndex = (regionX - centerX) + halfWidth;
int zIndex = (regionZ - centerZ) + halfWidth;
if (!regionIsInRange(regionX, regionZ))
// out of range
return null;
if (regions[xIndex][zIndex] == null)
{
regions[xIndex][zIndex] = getRegionFromFile(regionX, regionZ);
if (regions[xIndex][zIndex] == null)
{
regions[xIndex][zIndex] = new LodRegion(regionX, regionZ);
}
}
return regions[xIndex][zIndex];
}
/**
* Overwrite the LodRegion at the location of newRegion with newRegion.
* @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension.
*/
public void setRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
{
int xIndex = (newRegion.x - centerX) + halfWidth;
int zIndex = (centerZ - newRegion.z) + halfWidth;
if (!regionIsInRange(newRegion.x, newRegion.z))
// out of range
throw new ArrayIndexOutOfBoundsException();
regions[xIndex][zIndex] = newRegion;
}
/**
* Add the given LOD to this dimension at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinates it will be overwritten.
*/
public void addLod(LodChunk lod)
{
RegionPos pos = LodUtils.convertChunkPosToRegionPos(new ChunkPos(lod.x, lod.z));
// don't continue if the region can't be saved
if (!regionIsInRange(pos.x, pos.z))
{
return;
}
LodRegion region = getRegion(pos.x, pos.z);
if (region == null)
{
// if no region exists, create it
region = new LodRegion(pos.x, pos.z);
setRegion(region);
}
region.addLod(lod);
// don't save empty place holders to disk
if (!lod.isPlaceholder() && fileHandler != null)
{
// mark the region as dirty so it will be saved to disk
int xIndex = (pos.x - centerX) + halfWidth;
int zIndex = (pos.z - centerZ) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
fileHandler.saveDirtyRegionsToFileAsync();
}
}
/**
* Get the LodChunk at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public LodChunk getLodFromCoordinates(int chunkX, int chunkZ)
{
RegionPos pos = LodUtils.convertChunkPosToRegionPos(new ChunkPos(chunkX, chunkZ));
LodRegion region = getRegion(pos.x, pos.z);
if(region == null)
return null;
return region.getLod(chunkX, chunkZ);
}
/**
* Get the region at the given X and Z coordinates from the
* RegionFileHandler.
*/
public LodRegion getRegionFromFile(int regionX, int regionZ)
{
if (fileHandler != null)
return fileHandler.loadRegionFromFile(regionX, regionZ);
else
return null;
}
/**
* Returns whether the region at the given X and Z coordinates
* is within the loaded range.
*/
public boolean regionIsInRange(int regionX, int regionZ)
{
int xIndex = (regionX - centerX) + halfWidth;
int zIndex = (regionZ - centerZ) + halfWidth;
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
}
public int getCenterX()
{
return centerX;
}
public int getCenterZ()
{
return centerZ;
}
/**
* Returns how many non-null LodChunks
* are stored in this LodDimension.
*/
public int getNumberOfLods()
{
int numbLods = 0;
for (LodRegion[] regions : regions)
{
if(regions == null)
continue;
for (LodRegion region : regions)
{
if(region == null)
continue;
for(LodChunk[] lods : region.getAllLods())
{
if(lods == null)
continue;
for(LodChunk lod : lods)
{
if (lod != null)
numbLods++;
}
}
}
}
return numbLods;
}
public int getWidth()
{
return width;
}
public void setRegionWidth(int newWidth)
{
width = newWidth;
halfWidth = (int)Math.floor(width / 2);
regions = new LodRegion[width][width];
isRegionDirty = 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()
{
String s = "";
s += "dim: " + dimension.toString() + "\t";
s += "(" + centerX + "," + centerZ + ")";
return s;
}
}
@@ -0,0 +1,90 @@
package com.seibel.lod.objects;
/**
* A LodRegion is the a 32x32
* 2D array of LodChunk objects.
* Each LodRegion corresponds to
* one file in the file system.
*
* @author James Seibel
* @version 1-22-2021
*/
public class LodRegion
{
/** number of chunks wide */
public static final int SIZE = 32;
/** X coordinate of this region */
public final int x;
/** Z coordinate of this region */
public final int z;
private LodChunk chunks[][];
public LodRegion(int regionX, int regionZ)
{
x = regionX;
z = regionZ;
chunks = new LodChunk[SIZE][SIZE];
}
/**
* Add the given LOD to this region at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinates it will be overwritten.
*/
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 % SIZE);
int zIndex = Math.abs(lod.z % SIZE);
chunks[xIndex][zIndex] = lod;
}
/**
* Get the LodChunk at the given X and Z coordinates
* in this region.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public LodChunk getLod(int chunkX, int chunkZ)
{
// since we add LOD's with ABS, we get them the same way
int arrayX = Math.abs(chunkX % SIZE);
int arrayZ = Math.abs(chunkZ % SIZE);
if(arrayX >= SIZE || arrayZ >= SIZE)
return null;
return chunks[arrayX][arrayZ];
}
/**
* Returns all LodChunks in this region
*/
public LodChunk[][] getAllLods()
{
return chunks;
}
@Override
public String toString()
{
String s = "";
s += "x: " + x + " z: " + z + "\t";
return s;
}
}
@@ -0,0 +1,110 @@
package com.seibel.lod.objects;
import java.util.Hashtable;
import java.util.Map;
import net.minecraft.world.DimensionType;
/**
* This stores all LODs for a given world.
*
* @author James Seibel
* @version 04-01-2021
*/
public class LodWorld
{
private String worldName;
private Map<DimensionType, LodDimension> lodDimensions;
/** If true then the LOD world is setup and ready to use */
private boolean isWorldLoaded = false;
public static final String NO_WORLD_LOADED = "No world loaded";
public LodWorld()
{
worldName = NO_WORLD_LOADED;
}
/**
* Set up the LodWorld with the given newWorldName. <br>
* This should be done whenever loading a new world.
* @param newWorldName
*/
public void selectWorld(String newWorldName)
{
if(newWorldName.isEmpty())
{
deselectWorld();
return;
}
if (worldName.equals(newWorldName))
// don't recreate everything if we
// didn't actually change worlds
return;
worldName = newWorldName;
lodDimensions = new Hashtable<DimensionType, LodDimension>();
isWorldLoaded = true;
}
/**
* Set the worldName to "No world loaded"
* and clear the lodDimensions Map. <br>
* This should be done whenever unloaded a world.
*/
public void deselectWorld()
{
worldName = NO_WORLD_LOADED;
lodDimensions = null;
isWorldLoaded = false;
}
public void addLodDimension(LodDimension newStorage)
{
if (lodDimensions == null)
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
lodDimensions.put(newStorage.dimension, newStorage);
}
public LodDimension getLodDimension(DimensionType dimension)
{
if (lodDimensions == null)
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
return lodDimensions.get(dimension);
}
/**
* Resizes the max width in regions that each LodDimension
* should use.
*/
public void resizeDimensionRegionWidth(int newWidth)
{
if (lodDimensions == null)
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
for(DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newWidth);
}
public boolean getIsWorldLoaded()
{
return isWorldLoaded;
}
public String getWorldName()
{
return worldName;
}
@Override
public String toString()
{
return "World name: " + worldName;
}
}
@@ -0,0 +1,28 @@
package com.seibel.lod.objects;
import net.minecraft.client.renderer.BufferBuilder;
/**
* This object is just a replacement for an array
* to make things easier to understand in the LodRenderer
* and BuildBufferThread.
*
* @author James Seibel
* @version 03-25-2021
*/
public class NearFarBuffer
{
public BufferBuilder nearBuffer;
public BufferBuilder farBuffer;
/**
* @param newNearBuffer
* @param newFarBuffer
*/
public NearFarBuffer(BufferBuilder newNearBuffer, BufferBuilder newFarBuffer)
{
nearBuffer = newNearBuffer;
farBuffer = newFarBuffer;
}
}
@@ -0,0 +1,28 @@
package com.seibel.lod.objects;
import com.seibel.lod.enums.FogDistance;
/**
* This object is just a replacement for an array
* to make things easier to understand in the LodRenderer.
*
* @author James Seibel
* @version 02-27-2021
*/
public class NearFarFogSetting
{
public FogDistance nearFogSetting = FogDistance.NEAR;
public FogDistance farFogSetting = FogDistance.FAR;
public NearFarFogSetting()
{
}
public NearFarFogSetting(FogDistance newNearFogSetting, FogDistance newFarFogSetting)
{
nearFogSetting = newNearFogSetting;
farFogSetting = newFarFogSetting;
}
}
@@ -0,0 +1,31 @@
package com.seibel.lod.objects;
/**
* This object is similar to ChunkPos or BlockPos.
*
* @author James Seibel
* @version 03-19-2021
*/
public class RegionPos
{
public int x;
public int z;
/**
* Default Constructor <br>
*
* Sets x and z to 0
*/
public RegionPos()
{
x = 0;
z = 0;
}
public RegionPos(int newX, int newZ)
{
x = newX;
z = newZ;
}
}