diff --git a/src/main/java/com/seibel/lod/QuadTreeImage.java b/src/main/java/com/seibel/lod/QuadTreeImage.java index 3835ff0d0..c62106e3f 100644 --- a/src/main/java/com/seibel/lod/QuadTreeImage.java +++ b/src/main/java/com/seibel/lod/QuadTreeImage.java @@ -256,7 +256,7 @@ public class QuadTreeImage extends JPanel ((data.startBlockPos.getZ() - zOffset) * amp), data.width * amp, data.width * amp), - data.lodDataPoint.color, new BasicStroke(1))); + data.getLodDataPoint().color, new BasicStroke(1))); } myDrawables.add(new MyDrawable(new Rectangle2D.Double( (playerXs[0] - xOffset) * amp, diff --git a/src/main/java/com/seibel/lod/builders/lodNodeTemplates/CubicLodNodeTemplate.java b/src/main/java/com/seibel/lod/builders/lodNodeTemplates/CubicLodNodeTemplate.java index b04525f58..19e2abcaf 100644 --- a/src/main/java/com/seibel/lod/builders/lodNodeTemplates/CubicLodNodeTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodNodeTemplates/CubicLodNodeTemplate.java @@ -53,15 +53,15 @@ public class CubicLodNodeTemplate extends AbstractLodNodeTemplate // returns null if the lod is empty at the given location bbox = generateBoundingBox( - lod.lodDataPoint.height, - lod.lodDataPoint.depth, + lod.getLodDataPoint().height, + lod.getLodDataPoint().depth, lod.width, xOffset - halfWidth, yOffset, zOffset - halfWidth); if (bbox != null) { - addBoundingBoxToBuffer(buffer, bbox, lod.lodDataPoint.color); + addBoundingBoxToBuffer(buffer, bbox, lod.getLodDataPoint().color); } } 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 da26823c7..2040a29ac 100644 --- a/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodTemplates/CubicLodTemplate.java @@ -58,8 +58,8 @@ public class CubicLodTemplate extends AbstractLodTemplate // returns null if the lod is empty at the given location bbox = generateBoundingBox( - centerLod.lodDataPoint.height, - centerLod.lodDataPoint.depth, + centerLod.getLodDataPoint().height, + centerLod.getLodDataPoint().depth, detail.dataPointWidth, xOffset - (centerLod.width / 2), yOffset, @@ -67,7 +67,7 @@ public class CubicLodTemplate extends AbstractLodTemplate if (bbox != null) { - addBoundingBoxToBuffer(buffer, bbox, centerLod.lodDataPoint.color); + addBoundingBoxToBuffer(buffer, bbox, centerLod.getLodDataPoint().color); } } diff --git a/src/main/java/com/seibel/lod/objects/LodDataPoint.java b/src/main/java/com/seibel/lod/objects/LodDataPoint.java index 46d64a8ea..7278c551a 100644 --- a/src/main/java/com/seibel/lod/objects/LodDataPoint.java +++ b/src/main/java/com/seibel/lod/objects/LodDataPoint.java @@ -21,13 +21,14 @@ import java.awt.Color; import java.util.Objects; import com.seibel.lod.handlers.LodDimensionFileHandler; +import com.seibel.lod.util.LodUtil; /** * This stores the height and color * for a specific area in a LodChunk. * * @author James Seibel - * @version 6-19-2021 + * @version 8-8-2021 */ public class LodDataPoint { @@ -37,7 +38,9 @@ public class LodDataPoint /** this is how many pieces of data are exported when toData is called */ public static final int NUMBER_OF_DELIMITERS = 5; - private static final Color INVISIBLE = new Color(0,0,0,0); + /** a empty data point that can be used for comparisons */ + public static final LodDataPoint EMPTY_DATA_POINT = new LodDataPoint(); + /** highest point */ public short height; @@ -50,13 +53,13 @@ public class LodDataPoint /** - * Creates and empty LodDataPoint + * Creates an empty LodDataPoint */ public LodDataPoint() { height = -1; depth = -1; - color = INVISIBLE; + color = LodUtil.COLOR_INVISIBLE; } @@ -74,15 +77,23 @@ public class LodDataPoint color = newColor; } - public int hashCode(){ + @Override + public int hashCode() + { return Objects.hash(this.height, this.depth, this.color); } - public boolean equals(LodDataPoint other){ + public boolean equals(LodDataPoint other) + { return (this.height == other.height && this.depth == other.depth && this.color == other.color); } + + public boolean isEmpty() + { + return this.equals(EMPTY_DATA_POINT); + } /** * Outputs all data in a csv format diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTree.java b/src/main/java/com/seibel/lod/objects/LodQuadTree.java index ce838386a..e09ab48e7 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTree.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTree.java @@ -506,7 +506,7 @@ public class LodQuadTree } else { - this.lodNode.update(newLodQuadTreeNode); + this.lodNode.updateData(newLodQuadTreeNode); } //a recursive update is necessary to change the higher levels diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java b/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java index df167136a..b2764b8af 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java @@ -28,7 +28,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.gen.Heightmap; /** - * This object contains position + * This object contains position, size, * and color data for an LOD object. * * @author Leonardo Amato @@ -38,31 +38,26 @@ import net.minecraft.world.gen.Heightmap; public class LodQuadTreeNode { /** This is what separates each piece of data in the toData method */ - private static final char DATA_DELIMITER = LodQuadTreeDimensionFileHandler.DATA_DELIMITER; - - /** alpha used when drawing chunks in debug mode */ - private static final int DEBUG_ALPHA = 255; // 0 - 255 - @SuppressWarnings("unused") - private static final Color DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA); - @SuppressWarnings("unused") - private static final Color DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA); - @SuppressWarnings("unused") - private static final Color INVISIBLE = new Color(0,0,0,0); - + private static final char DATA_DELIMITER_COUNT = LodQuadTreeDimensionFileHandler.DATA_DELIMITER; /** If we ever have to use a heightmap for any reason, use this one. */ public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; + /** this is how many pieces of data are exported when toData is called */ + public static final int NUMBER_OF_DELIMITERS = 10; + + /** If this is set to true then toData will return * the empty string */ public boolean dontSave = false; - - - /** this is how many pieces of data are exported when toData is called */ - public static final int NUMBER_OF_DELIMITERS = 10; - + // these 2 values indicate the position of the LOD in the relative Level + // this will be useful in the generation process + /** X position relative to the Quad tree. */ + public final int posX; + /** Z position relative to the Quad tree */ + public final int posZ; //Complexity indicate how the block was built. This is important because we could use public DistanceGenerationMode complexity; @@ -87,25 +82,23 @@ public class LodQuadTreeNode /** detail level 0 */ public static final short BLOCK_WIDTH = 1; - // these 2 values indicate the position of the LOD in the relative Level - // this will be useful in the generation process - /** X position relative to the Quad tree. */ - public final int posX; - /** Z position relative to the Quad tree */ - public final int posZ; - //these 4 value indicate the corner of the LOD block - //they can be named SW, SE, NW, NE as the cardinal direction. - //the start values should always be smaller than the end values. - //All this value could be calculated from level, posx and posz - //so they could be removed and replaced with just a getter + // these 4 value indicate the corner of the LOD block + // they can be named SW, SE, NW, NE as the cardinal direction. + // the start values should always be smaller than the end values. + // All this value could be calculated from level, posx, and posz + // so they could be removed and replaced with just a getter public final BlockPos startBlockPos; public final BlockPos endBlockPos; - //these 2 value indicate the center of the LodNode in real coordinate. This - //can be used to calculate the distance from the player + + /** + * Indicates the center of the LodNode in absolute block coordinates. This + * can be used to calculate distance from the player. + */ public final BlockPos center; - public LodDataPoint lodDataPoint; + /** holds the height, depth, and color data for this Node. */ + private LodDataPoint lodDataPoint; /** if true this node doesn't have any data */ public boolean voidNode; @@ -212,45 +205,61 @@ public class LodQuadTreeNode dontSave = false; } - public LodQuadTreeNode(String data) + /** + * @throws IllegalArgumentException if the data string doesn't have the correct number of delimited entries + */ + public LodQuadTreeNode(String data) throws IllegalArgumentException { + // make sure there are the correct number of entries + // in the data string + int count = 0; + + for(int i = 0; i < data.length(); i++) + if(data.charAt(i) == DATA_DELIMITER_COUNT) + count++; + + if(count != DATA_DELIMITER_COUNT) + throw new IllegalArgumentException("LodQuadTreeNode constructor givin an invalid string. The data given had " + count + " delimiters when it should have had " + DATA_DELIMITER_COUNT + "."); + + + int index = 0; int lastIndex = 0; - index = data.indexOf(DATA_DELIMITER, 0); + index = data.indexOf(DATA_DELIMITER_COUNT, 0); this.detailLevel = (byte) Integer.parseInt(data.substring(0,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); this.posX = Integer.parseInt(data.substring(lastIndex+1,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); this.posZ = Integer.parseInt(data.substring(lastIndex+1,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); this.complexity = DistanceGenerationMode.valueOf(data.substring(lastIndex+1,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); short height = (short) Integer.parseInt(data.substring(lastIndex+1,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); short depth = (short) Integer.parseInt(data.substring(lastIndex+1,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); int r = Integer.parseInt(data.substring(lastIndex+1,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); int g = Integer.parseInt(data.substring(lastIndex+1,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); int b = Integer.parseInt(data.substring(lastIndex+1,index)); lastIndex = index; - index = data.indexOf(DATA_DELIMITER, lastIndex+1); + index = data.indexOf(DATA_DELIMITER_COUNT, lastIndex+1); int a = Integer.parseInt(data.substring(lastIndex+1,index)); Color color = new Color(r,g,b,a); lodDataPoint = new LodDataPoint(height,depth,color); @@ -272,21 +281,29 @@ public class LodQuadTreeNode + //================// + // data processes // + //================// - public void update(LodQuadTreeNode lodQuadTreeNode) + /** + * Replaces the data in this object. + */ + public void updateData(LodQuadTreeNode lodQuadTreeNode) { - this.lodDataPoint = lodQuadTreeNode.lodDataPoint; - this.complexity = lodQuadTreeNode.complexity; - this.voidNode = lodQuadTreeNode.voidNode; + if (lodQuadTreeNode == null) + return; + + lodDataPoint = lodQuadTreeNode.lodDataPoint; + complexity = lodQuadTreeNode.complexity; + voidNode = lodQuadTreeNode.voidNode; + dontSave = lodQuadTreeNode.dontSave; + dirty = true; - dontSave = false; - } - - public LodDataPoint getLodDataPoint() - { - return lodDataPoint; } + /** + * Combines and averages the data from a list of LodQuadTreeNodes into this node. + */ public void combineData(List dataList) { if(dataList.isEmpty()) @@ -295,15 +312,22 @@ public class LodQuadTreeNode } else { + // TODO would it be better to use the average height/depth? + // get the lowest height from the all the given LodQuadTreeNodes short height = (short) dataList.stream().mapToInt(x -> (int) x.getLodDataPoint().height).min().getAsInt(); + // get the highest depth short depth = (short) dataList.stream().mapToInt(x -> (int) x.getLodDataPoint().depth).max().getAsInt(); - int red = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getRed()).sum()/dataList.size(); - int green = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getGreen()).sum()/dataList.size(); - int blue = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getBlue()).sum()/dataList.size(); + + // get the average color + int red = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getRed()).sum() / dataList.size(); + int green = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getGreen()).sum() / dataList.size(); + int blue = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getBlue()).sum() / dataList.size(); Color color = new Color(red,green,blue); + lodDataPoint = new LodDataPoint(height,depth,color); - //the new complexity equal to the lowest complexity of the list + + // the new complexity is equal to the lowest complexity of the list DistanceGenerationMode minComplexity = DistanceGenerationMode.SERVER; for(LodQuadTreeNode node: dataList) { @@ -315,7 +339,7 @@ public class LodQuadTreeNode complexity = minComplexity; - voidNode = dataList.stream().filter(x -> !x.voidNode).count() == 0; + voidNode = lodDataPoint.isEmpty(); } dirty = true; @@ -323,18 +347,16 @@ public class LodQuadTreeNode } - @Override - public int hashCode() - { - return Objects.hash(this.complexity, this.detailLevel, this.posX, this.posZ, this.lodDataPoint, this.voidNode); - } + + //===================// + // basic comparisons // + //===================// public int compareComplexity(LodQuadTreeNode other) { return this.complexity.compareTo(other.complexity); } - public boolean equals(LodQuadTreeNode other) { return (this.complexity == other.complexity @@ -347,6 +369,24 @@ public class LodQuadTreeNode } + + //=========================// + // basic setters / getters // + //=========================// + + public LodDataPoint getLodDataPoint() + { + return lodDataPoint; + } + + public void setLodDataPoint(LodDataPoint newLodDataPoint) + { + lodDataPoint = newLodDataPoint; + + // update if this is node is currently void + voidNode = (lodDataPoint == null); + } + public boolean isVoidNode() { return voidNode; @@ -358,6 +398,18 @@ public class LodQuadTreeNode } + + + //========// + // output // + //========// + + @Override + public int hashCode() + { + return Objects.hash(this.complexity, this.detailLevel, this.posX, this.posZ, this.lodDataPoint, this.voidNode); + } + /** * Outputs all data in a csv format */ @@ -366,16 +418,16 @@ public class LodQuadTreeNode if (dontSave) return ""; - String s = Integer.toString(detailLevel) + DATA_DELIMITER - + Integer.toString(posX) + DATA_DELIMITER - + Integer.toString(posZ) + DATA_DELIMITER - + complexity.toString() + DATA_DELIMITER - + Integer.toString((lodDataPoint.height)) + DATA_DELIMITER - + Integer.toString((lodDataPoint.depth)) + DATA_DELIMITER - + Integer.toString(lodDataPoint.color.getRed()) + DATA_DELIMITER - + Integer.toString(lodDataPoint.color.getGreen()) + DATA_DELIMITER - + Integer.toString(lodDataPoint.color.getBlue()) + DATA_DELIMITER - + Integer.toString(lodDataPoint.color.getAlpha()) + DATA_DELIMITER + String s = Integer.toString(detailLevel) + DATA_DELIMITER_COUNT + + Integer.toString(posX) + DATA_DELIMITER_COUNT + + Integer.toString(posZ) + DATA_DELIMITER_COUNT + + complexity.toString() + DATA_DELIMITER_COUNT + + Integer.toString((lodDataPoint.height)) + DATA_DELIMITER_COUNT + + Integer.toString((lodDataPoint.depth)) + DATA_DELIMITER_COUNT + + Integer.toString(lodDataPoint.color.getRed()) + DATA_DELIMITER_COUNT + + Integer.toString(lodDataPoint.color.getGreen()) + DATA_DELIMITER_COUNT + + Integer.toString(lodDataPoint.color.getBlue()) + DATA_DELIMITER_COUNT + + Integer.toString(lodDataPoint.color.getAlpha()) + DATA_DELIMITER_COUNT + Integer.toString(voidNode ? 1 : 0); return s; } diff --git a/src/main/java/com/seibel/lod/render/LodNodeRenderer.java b/src/main/java/com/seibel/lod/render/LodNodeRenderer.java index 1deddc9fd..c3664c441 100644 --- a/src/main/java/com/seibel/lod/render/LodNodeRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodNodeRenderer.java @@ -831,7 +831,7 @@ public class LodNodeRenderer LodQuadTreeNode lod = lodDim.getLodFromCoordinates(new ChunkPos(x, z), 4); if (lod != null) { - short lodHighestPoint = lod.lodDataPoint.height; + short lodHighestPoint = lod.getLodDataPoint().height; if (playerPos.getY() < lodHighestPoint) { diff --git a/src/main/java/com/seibel/lod/util/LodUtil.java b/src/main/java/com/seibel/lod/util/LodUtil.java index b4797ae1f..21d4a1acb 100644 --- a/src/main/java/com/seibel/lod/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/util/LodUtil.java @@ -34,16 +34,22 @@ import net.minecraft.world.server.ServerChunkProvider; import net.minecraft.world.server.ServerWorld; /** - * This class holds methods that may be used in multiple places. + * This class holds methods and constants that may be used in multiple places. * * @author James Seibel - * @version 06-27-2021 + * @version 8-8-2021 */ public class LodUtil { private static Minecraft mc = Minecraft.getInstance(); + /** alpha used when drawing chunks in debug mode */ + public static final int DEBUG_ALPHA = 255; // 0 - 255 + public static final Color COLOR_DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA); + public static final Color COLOR_DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA); + public static final Color COLOR_INVISIBLE = new Color(0,0,0,0); + /** * Gets the first valid ServerWorld.