Add variable detail LODs

This commit is contained in:
James Seibel
2021-06-12 18:19:45 -05:00
parent bf6db89a4b
commit 3694dcba4c
9 changed files with 554 additions and 422 deletions
@@ -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)
{
@@ -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);
@@ -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,
@@ -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<RelativeChunkPos> 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;
}
}
@@ -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++;
}
}
}
}
@@ -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(() ->
+238 -155
View File
@@ -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.
* <br>
* 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;
}
@@ -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.
* <br>
* Exports data in the form:
* <br>
* height, depth, rgb color data
*
* <br>
* example output:
* <br>
* 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;
}
}
@@ -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];
}