From 9e24ee0ef7b39c6adf04876aaf44c92cb20034c5 Mon Sep 17 00:00:00 2001 From: Morippi Date: Wed, 7 Jul 2021 00:53:32 +0200 Subject: [PATCH] Fixed LodQuadTree Added QuadTreeImage to visualize how the quadTree is builded --- build.gradle | 31 +- .../seibel/lod/builders/LodNodeBuilder.java | 377 ++++++++++++++---- .../lod/objects/quadTree/LodNodeData.java | 1 + .../lod/objects/quadTree/LodQuadTree.java | 128 ++++-- .../quadTree/LodQuadTreeDimension.java | 12 +- .../lod/objects/quadTree/QuadTreeImage.java | 195 +++++++++ .../lod/objects/quadTree/UsesExamples.java | 32 +- 7 files changed, 637 insertions(+), 139 deletions(-) create mode 100644 src/main/java/com/seibel/lod/objects/quadTree/QuadTreeImage.java diff --git a/build.gradle b/build.gradle index f326b9a3b..c253ceb27 100644 --- a/build.gradle +++ b/build.gradle @@ -116,12 +116,39 @@ minecraft { // Include resources generated by data generators. sourceSets.main.resources { srcDir 'src/generated/resources' } +repositories { + mavenCentral() + maven { url 'https://jitpack.io' } +} + dependencies { // Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed // that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied. // The userdev artifact is a special name and will get all sorts of transformations applied to it. - minecraft 'net.minecraftforge:forge:1.16.5-36.1.0' - + minecraft 'net.minecraftforge:forge:1.16.4-35.1.4' + + /* + classpath group: 'com.github.KaptainWutax', name: 'BiomeUtils-lib', version: '-SNAPSHOT' + classpath group: 'com.github.KaptainWutax', name: 'SeedUtils', version: '-SNAPSHOT' + classpath group: 'com.github.KaptainWutax', name: 'MCUtils', version: '-SNAPSHOT' + classpath group: 'com.github.KaptainWutax', name: 'NoiseUtils', version: 'main-SNAPSHOT' + classpath group: 'com.github.KaptainWutax', name: 'TerrainUtils', version: '-SNAPSHOT' + */ + compile('com.github.KaptainWutax:BiomeUtils:1.0.0') { + transitive = false + } + compile('com.github.KaptainWutax:SeedUtils:-SNAPSHOT') { + transitive = false + } + compile('com.github.KaptainWutax:MCUtils:1.0.0') { + transitive = false + } + compile('com.github.KaptainWutax:NoiseUtils:1.0.0') { + transitive = false + } + compile('com.github.KaptainWutax:TerrainUtils:1.0.0') { + transitive = false + } // these were added to hopefully allow for cloning // configuredFeatures to allow for safe diff --git a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java index 5f2d00988..3bc365d54 100644 --- a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java @@ -1,25 +1,20 @@ package com.seibel.lod.builders; -import com.seibel.lod.enums.LodDetail; -import com.seibel.lod.handlers.LodConfig; 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.objects.quadTree.LodNodeData; +import com.seibel.lod.objects.quadTree.LodQuadTreeDimension; import com.seibel.lod.objects.quadTree.LodQuadTreeWorld; -import kaptainwutax.biomeutils.source.BiomeSource; -import kaptainwutax.biomeutils.source.OverworldBiomeSource; -import kaptainwutax.mcutils.state.Dimension; -import kaptainwutax.mcutils.version.MCVersion; -import net.minecraft.util.ResourceLocation; +import com.seibel.lod.util.LodUtil; +import net.minecraft.block.*; import net.minecraft.world.DimensionType; import net.minecraft.world.IWorld; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.gen.Heightmap; import java.awt.*; -import java.util.Iterator; -import java.util.OptionalLong; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -28,24 +23,32 @@ public class LodNodeBuilder { private long seed; private DimensionType dimension; - /** Default size of any LOD regions we use */ + public static final int CHUNK_DATA_WIDTH = LodNodeData.CHUNK_WIDTH; + public static final int CHUNK_SECTION_HEIGHT = 256; + public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG; + + /** + * Default size of any LOD regions we use + */ public int regionWidth = 5; - /** fast biome calculator */ - private BiomeSource biomeSource; + /** + * fast biome calculator + */ + //private BiomeSource biomeSource; //Biome biome=biomeSource.getBiome(x,y,z); // here y is always 0 no matter what you pass - public LodNodeBuilder(){ + public LodNodeBuilder() { } +/* public setApproxGenerator(long seed){ //Dimension.OVERWORLD; //Dimension.END; //Dimension.NETHER; biomeSource = BiomeSource.of(Dimension.OVERWORLD ,MCVersion.v1_16_4, seed); } - public void generateLodNodeAsync(List dataList){ Thread thread = new Thread(() ->{ for(LodNodeData data : dataList){ @@ -57,9 +60,9 @@ public class LodNodeBuilder { return; } + */ - public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world) - { + public void generateLodNodeAsync(IChunk chunk, LodQuadTreeWorld lodWorld, IWorld world) { if (lodWorld == null || !lodWorld.getIsWorldLoaded()) return; @@ -71,28 +74,22 @@ public class LodNodeBuilder { Thread thread = new Thread(() -> { - try - { - DimensionType dim = world.getDimensionType(); + try { + DimensionType dim = world.dimensionType(); - LodChunk lod = generateLodFromChunk(chunk, config); + LodNodeData node = generateLodNodeFromChunk(chunk); - LodDimension lodDim; + LodQuadTreeDimension lodDim; - if (lodWorld.getLodDimension(dim) == null) - { - lodDim = new LodDimension(dim, lodWorld, regionWidth); + if (lodWorld.getLodDimension(dim) == null) { + lodDim = new LodQuadTreeDimension(dim, lodWorld, regionWidth); lodWorld.addLodDimension(lodDim); - } - else - { + } else { lodDim = lodWorld.getLodDimension(dim); } - lodDim.addLod(lod); - } - catch(IllegalArgumentException | NullPointerException e) - { + lodDim.addNode(node); + } catch (IllegalArgumentException | NullPointerException e) { // if the world changes while LODs are being generated // they will throw errors as they try to access things that no longer // exist. @@ -104,69 +101,295 @@ public class LodNodeBuilder { } - - - /** * Creates a LodChunk for a chunk in the given world. * - * @throws IllegalArgumentException - * thrown if either the chunk or world is null. + * @throws IllegalArgumentException thrown if either the chunk or world is null. */ - public LodChunk generateLodFromChunk(IChunk chunk) throws IllegalArgumentException - { - return generateLodFromChunk(chunk, new LodBuilderConfig()); + public LodNodeData generateLodNodeFromChunk(IChunk chunk) throws IllegalArgumentException { + return generateLodNodeFromChunk(chunk, new LodBuilderConfig()); } /** * Creates a LodChunk for a chunk in the given world. * - * @throws IllegalArgumentException - * thrown if either the chunk or world is null. + * @throws IllegalArgumentException thrown if either the chunk or world is null. + * @return */ - public LodChunk generateLodFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException - { - if(chunk == null) + public LodNodeData generateLodNodeFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException { + if (chunk == null) throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); - LodDetail detail = LodConfig.CLIENT.lodDetail.get(); - LodDataPoint[][] dataPoints = new LodDataPoint[detail.lengthCount][detail.lengthCount]; + int startX = chunk.getPos().getMinBlockX(); + int startZ = chunk.getPos().getMinBlockZ(); + int endX = chunk.getPos().getMaxBlockX(); + int endZ = chunk.getPos().getMaxBlockZ(); - for(int i = 0; i < detail.lengthCount * detail.lengthCount; i++) - { - int startX = detail.startX[i]; - int startZ = detail.startZ[i]; - int endX = detail.endX[i]; - int endZ = detail.endZ[i]; + Color color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ); - Color color; + short height; + short depth; - color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ); + /**TODO I HAVE TO RE-ADD THE HEIGHTMAP**/ + height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ); + depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ); - short height; - short depth; - - if (!config.useHeightmap) - { - height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ); - depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ); - } - else - { - height = determineHeightPoint(chunk.getHeightmap(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ); - depth = 0; - } - - int x = i / detail.lengthCount; - int z = i % detail.lengthCount; - - dataPoints[x][z] = new LodDataPoint(height, depth, color); - } - - return new LodChunk(chunk.getPos(), dataPoints, detail); + return new LodNodeData(LodNodeData.CHUNK_LEVEL, chunk.getPos().x, chunk.getPos().z, height, depth, color, true); } + //=====================// + // constructor helpers // + //=====================// + + + /** + * Find the lowest valid point from the bottom. + * + * @param chunkSections + * @param startX + * @param startZ + * @param endX + * @param endZ + */ + 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 section = 0; section < CHUNK_DATA_WIDTH; section++) { + for (int y = 0; y < CHUNK_SECTION_HEIGHT; y++) { + int numberOfBlocksFound = 0; + + for (int x = startX; x < endX; x++) { + 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)); + } + } + } + } + } + } + + // we never found a valid LOD point + return -1; + } + + /** + * Find the lowest valid point from the bottom. + */ + @SuppressWarnings("unused") + private short determineBottomPoint(Heightmap heightmap) { + // the heightmap only shows how high the blocks go, it + // doesn't have any info about how low they go + return 0; + } + + + /** + * Find the highest valid point from the Top + * + * @param chunkSections + * @param startX + * @param startZ + * @param endX + * @param endZ + */ + private short determineHeightPointForArea(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--) { + int numberOfBlocksFound = 0; + + for (int x = startX; x < endX; x++) { + 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)); + } + } + } + } + } + } + + // we never found a valid LOD point + return -1; + } + + /** + * Find the highest point from the Top + */ + private short determineHeightPoint(Heightmap heightmap, + int startX, int startZ, int endX, int endZ) { + short highest = 0; + for (int x = startX; x < endX; x++) { + for (int z = startZ; z < endZ; z++) { + short newHeight = (short) heightmap.getFirstAvailable(x, z); + if (newHeight > highest) + highest = newHeight; + } + } + + return highest; + } + + /** + * Generate the color for the given chunk using biome + * water color, foliage color, and grass color. + * + * @param config_useSolidBlocksInColorGen
+ * If true we look down from the top of the
+ * chunk until we find a non-invisible block, and then use
+ * its color. If false we generate the color immediately for
+ * each x and z. + * @param config_useBiomeColors
+ * If true use biome foliage, water, and grass colors,
+ * otherwise use the + */ + private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, int endZ) { + ChunkSection[] chunkSections = chunk.getSections(); + + int numbOfBlocks = 0; + int red = 0; + int green = 0; + int blue = 0; + + + for (int x = startX; x < endX; x++) { + for (int z = startZ; z < endZ; z++) { + boolean foundBlock = false; + + // go top down + for (int i = chunkSections.length - 1; !foundBlock && i >= 0; i--) { + if (!foundBlock && (chunkSections[i] != null || !config.useSolidBlocksInColorGen)) { + for (int y = CHUNK_SECTION_HEIGHT - 1; !foundBlock && y >= 0; y--) { + int colorInt = 0; + BlockState blockState = null; + + if (chunkSections[i] != null) { + blockState = chunkSections[i].getBlockState(x, y, z); + colorInt = blockState.materialColor.col; + } + + if (colorInt == 0 && config.useSolidBlocksInColorGen) { + // skip air or invisible blocks + continue; + } + + if (config.useBiomeColors) { + Biome biome = chunk.getBiomes().getNoiseBiome(x, y + i * chunkSections.length, z); + + if (biome.getBiomeCategory() == Biome.Category.OCEAN || + biome.getBiomeCategory() == Biome.Category.RIVER) { + colorInt = biome.getWaterColor(); + } else if (biome.getBiomeCategory() == Biome.Category.EXTREME_HILLS) { + colorInt = Blocks.STONE.defaultMaterialColor().col; + } else if (biome.getBiomeCategory() == Biome.Category.ICY) { + colorInt = LodUtil.colorToInt(Color.WHITE); + } else if (biome.getBiomeCategory() == Biome.Category.THEEND) { + colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col; + } else if (config.useSolidBlocksInColorGen) { + colorInt = getColorForBlock(x, z, blockState, biome); + } else { + colorInt = biome.getGrassColor(x, z); + } + } else { + Biome biome = chunk.getBiomes().getNoiseBiome(x, y + i * chunkSections.length, z); + colorInt = getColorForBlock(x, z, blockState, biome); + } + + + Color c = LodUtil.intToColor(colorInt); + + red += c.getRed(); + green += c.getGreen(); + blue += c.getBlue(); + + numbOfBlocks++; + + + // we found a valid block, skip to the + // next x and z + foundBlock = true; + } + } + } + + } + } + + if (numbOfBlocks == 0) + numbOfBlocks = 1; + + red /= numbOfBlocks; + green /= numbOfBlocks; + blue /= numbOfBlocks; + + return new Color(red, green, blue); + } + + + /** + * Returns a color int for a given block. + */ + private int getColorForBlock(int x, int z, BlockState blockState, Biome biome) { + int colorInt = 0; + + if (blockState == Blocks.AIR.defaultBlockState()) { + colorInt = biome.getGrassColor(x, z); + } else if (blockState.getBlock() instanceof LeavesBlock) { + Color leafColor = LodUtil.intToColor(biome.getFoliageColor()).darker(); + colorInt = LodUtil.colorToInt(leafColor); + } else if (blockState.getBlock() instanceof GrassBlock) { + colorInt = biome.getGrassColor(x, z); + } else if (blockState.getBlock() instanceof FlowingFluidBlock) { + colorInt = biome.getWaterColor(); + } else { + colorInt = blockState.materialColor.col; + } + + return colorInt; + } + + + /** + * Is the layer between the given X, Z, and dataIndex + * values a valid LOD point? + */ + private boolean isLayerValidLodPoint( + ChunkSection[] chunkSections, + int sectionIndex, int y, + int x, int z) { + 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) { + return true; + } + } + + return false; + } } diff --git a/src/main/java/com/seibel/lod/objects/quadTree/LodNodeData.java b/src/main/java/com/seibel/lod/objects/quadTree/LodNodeData.java index b354ee826..b341dd2c2 100644 --- a/src/main/java/com/seibel/lod/objects/quadTree/LodNodeData.java +++ b/src/main/java/com/seibel/lod/objects/quadTree/LodNodeData.java @@ -62,6 +62,7 @@ public class LodNodeData { //if dirty is true, then this node have unsaved changes public boolean dirty; + /** * Creates and empty LodDataPoint * This LodDataPoint only contains the position data diff --git a/src/main/java/com/seibel/lod/objects/quadTree/LodQuadTree.java b/src/main/java/com/seibel/lod/objects/quadTree/LodQuadTree.java index 9202b1c7c..ef5c8e149 100644 --- a/src/main/java/com/seibel/lod/objects/quadTree/LodQuadTree.java +++ b/src/main/java/com/seibel/lod/objects/quadTree/LodQuadTree.java @@ -3,7 +3,6 @@ package com.seibel.lod.objects.quadTree; import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; - /** * This object contains all data useful to render LodBlock in a region (32x32 chunk o 512x512 block) * for every node it contains the border of the block, the size, the position at it's level, the color, the height and the depth. @@ -36,7 +35,7 @@ public class LodQuadTree { //data useful to render //if children are present then lodNodeData should be a combination of the lodData of the child. This can be //turned off by deselecting the recursive update in all update method. - private final LodNodeData lodNodeData; + private LodNodeData lodNodeData; /* .____.____. | NW | NE | | @@ -137,8 +136,8 @@ public class LodQuadTree { int posX = newLodNodeData.posX; int posZ = newLodNodeData.posZ; short widthRatio = (short) (lodNodeData.width / newLodNodeData.width); - int NS = (posX / widthRatio) % lodNodeData.posX; - int WE = (posZ / widthRatio) % lodNodeData.posZ; + int NS = (posX / widthRatio) % 2; + int WE = (posZ / widthRatio) % 2; if (getChild(NS, WE) == null) { setChild(NS, WE); } @@ -147,10 +146,10 @@ public class LodQuadTree { return false; } else { if (targetLevel == currentLevel - 1) { - child.setLodNodeData(lodNodeData, updateHigherLevel); + child.setLodNodeData(newLodNodeData, true); return true; } else { - return child.setNodeAtLowerLevel(lodNodeData, updateHigherLevel); + return child.setNodeAtLowerLevel(newLodNodeData, updateHigherLevel); } } } else { @@ -159,6 +158,32 @@ public class LodQuadTree { } + /** + * @param posX + * @param posZ + * @param level + * @return + */ + public LodNodeData getNodeAtLevelPosition(int posX, int posZ, byte level) { + byte targetLevel = level; + byte currentLevel = lodNodeData.level; + if (targetLevel == currentLevel) { + return lodNodeData; + } else if (targetLevel < currentLevel) { + short widthRatio = (short) (lodNodeData.width / Math.pow(2, level)); + int NS = (posX / widthRatio) % lodNodeData.posX; + int WE = (posZ / widthRatio) % lodNodeData.posZ; + if (getChild(NS, WE) == null) { + return lodNodeData; + } + LodQuadTree child = getChild(NS, WE); + return child.getNodeAtLevelPosition(posX, posZ, level); + } else { + return null; + } + + } + public LodQuadTree getChild(int NS, int WE) { return children[NS][WE]; } @@ -223,7 +248,7 @@ public class LodQuadTree { nodeFull = isFull; nodeEmpty = isEmpty; lodNodeData.combineData(dataList); - if (lodNodeData.level > 0 && recursiveUpdate) { + if (lodNodeData.level < 9 && recursiveUpdate) { this.parent.updateLevel(recursiveUpdate); } } @@ -262,20 +287,26 @@ public class LodQuadTree { } /** - * method to get certain nodes from the LodQuadTree - * @return list of nodes + * This method will return all the nodes that can be rendered based on the data given + * + * @param x position of the player + * @param z position of the player + * @param targetLevel minimum level that can be rendered + * @param maxDistance maximum distance from the player + * @param minDistance minimum distance from the player + * @return */ - public List getNodeToRender(int x, int z, byte targetLevel, int maxDistance, int minDistance){ - int distance = (int) Math.sqrt(Math.pow(x + lodNodeData.centerX,2) + Math.pow(z + lodNodeData.centerZ,2)); + public List getNodeToRender(int x, int z, byte targetLevel, int maxDistance, int minDistance) { + int distance = (int) Math.sqrt(Math.pow(x + lodNodeData.centerX, 2) + Math.pow(z + lodNodeData.centerZ, 2)); List nodeList = new ArrayList<>(); - if(distance > maxDistance || distance < minDistance || targetLevel > lodNodeData.level) { + if (distance > maxDistance || distance < minDistance || targetLevel < lodNodeData.level) { return nodeList; } - if(targetLevel == lodNodeData.level || !isThereAnyChild()){ - if(!lodNodeData.voidNode){ + if (targetLevel == lodNodeData.level || !isThereAnyChild()) { + if (!lodNodeData.voidNode) { nodeList.add(lodNodeData); return nodeList; - }else{ + } else { return nodeList; } } else { @@ -283,7 +314,7 @@ public class LodQuadTree { for (int WE = 0; WE <= 1; WE++) { LodQuadTree child = children[NS][WE]; if (child != null) { - nodeList.addAll(child.getNodeToRender(x,z,targetLevel,maxDistance,minDistance)); + nodeList.addAll(child.getNodeToRender(x, z, targetLevel, maxDistance, minDistance)); } } } @@ -292,33 +323,40 @@ public class LodQuadTree { } /** - * method to get certain nodes from the LodQuadTree - * @return list of nodes + * Nodes that can be generated in the approximated version + * A level is generated only if it has child and is higher than the target level + * @param x + * @param z + * @param targetLevel + * @param maxDistance + * @param minDistance + * @return */ - public List> getNodeToGenerate(int x, int z, byte targetLevel, int maxDistance, int minDistance){ - int distance = (int) Math.sqrt(Math.pow(x + lodNodeData.centerX,2) + Math.pow(z + lodNodeData.centerZ,2)); - List> nodeList = new ArrayList<>(); - if(distance > maxDistance || distance < minDistance || targetLevel > lodNodeData.level) { + public List> getLevelToGenerate(int x, int z, byte targetLevel, int maxDistance, int minDistance) { + int distance = (int) Math.sqrt(Math.pow(x + lodNodeData.centerX, 2) + Math.pow(z + lodNodeData.centerZ, 2)); + List> nodeList = new ArrayList<>(); + if (distance > maxDistance || distance < minDistance || targetLevel > lodNodeData.level || lodNodeData.real) { return nodeList; } - if(targetLevel == lodNodeData.level){ - return nodeList; - } else { - if(!isThereAnyChild()){ + if(isThereAnyChild()) { + //THIS LEVEL HAS CHILD SO IT'S GENERATED. + if (targetLevel != lodNodeData.level) { for (int NS = 0; NS <= 1; NS++) { for (int WE = 0; WE <= 1; WE++) { - LodQuadTree child = children[NS][WE]; - if (child != null) { - nodeList.addAll(child.getNodeToGenerate(x,z,targetLevel,maxDistance,minDistance)); + if (children[NS][WE] == null) { + setChild(NS,WE); } + LodQuadTree child = children[NS][WE]; + nodeList.addAll(child.getLevelToGenerate(x, z, targetLevel, maxDistance, minDistance)); } } - }else{ - nodeList.add( new AbstractMap.SimpleEntry<>(lodNodeData,distance)); } + } else { + nodeList.add(new AbstractMap.SimpleEntry<>(this, distance)); } return nodeList; } + /** * simple getter for lodNodeData * @@ -335,7 +373,11 @@ public class LodQuadTree { * @param updateHigherLevel if true it will update all the upper levels. */ public void setLodNodeData(LodNodeData newLodNodeData, boolean updateHigherLevel) { - this.lodNodeData.update(lodNodeData); + if (this.lodNodeData == null) { + this.lodNodeData = newLodNodeData; + } else { + this.lodNodeData.update(newLodNodeData); + } //a recursive update is necessary to change higher level if (parent != null && updateHigherLevel) parent.updateLevel(true); } @@ -345,7 +387,7 @@ public class LodQuadTree { } public boolean isThereAnyChild() { - return nodeEmpty; + return !nodeEmpty; } public boolean isNodeReal() { @@ -355,9 +397,19 @@ public class LodQuadTree { public boolean isRenderable() { return (lodNodeData != null); } -} -/* -EXAMPLES OF USES - - */ + public String toString(){ + String s = lodNodeData.toString(); + if(isThereAnyChild()){ + for (int NS = 0; NS <= 1; NS++) { + for (int WE = 0; WE <= 1; WE++) { + LodQuadTree child = children[NS][WE]; + if (child != null) { + s += '\n' + child.toString(); + } + } + } + } + return s; + } +} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/objects/quadTree/LodQuadTreeDimension.java b/src/main/java/com/seibel/lod/objects/quadTree/LodQuadTreeDimension.java index c864f6134..33e4a5bc4 100644 --- a/src/main/java/com/seibel/lod/objects/quadTree/LodQuadTreeDimension.java +++ b/src/main/java/com/seibel/lod/objects/quadTree/LodQuadTreeDimension.java @@ -29,7 +29,7 @@ public class LodQuadTreeDimension { private LodQuadTreeDimensionFileHandler fileHandler; - public LodQuadTreeDimension(DimensionType newDimension, LodWorld lodWorld, int newMaxWidth) + public LodQuadTreeDimension(DimensionType newDimension, LodQuadTreeWorld lodWorld, int newMaxWidth) { dimension = newDimension; width = newMaxWidth; @@ -39,7 +39,7 @@ public class LodQuadTreeDimension { Minecraft mc = Minecraft.getInstance(); File saveDir; - if(mc.isIntegratedServerRunning()) + if(mc.hasSingleplayerServer()) { // local world @@ -47,15 +47,15 @@ public class LodQuadTreeDimension { seed = serverWorld.getSeed(); // provider needs a separate variable to prevent // the compiler from complaining - ServerChunkProvider provider = serverWorld.getChunkProvider(); - saveDir = new File(provider.getSavedData().folder.getCanonicalFile().getPath() + File.separatorChar + "lod"); + ServerChunkProvider provider = serverWorld.getChunkSource(); + saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod"); } else { // connected to server - saveDir = new File(mc.gameDir.getCanonicalFile().getPath() + - File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.world)); + saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() + + File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level)); } fileHandler = new LodQuadTreeDimensionFileHandler(saveDir, this); diff --git a/src/main/java/com/seibel/lod/objects/quadTree/QuadTreeImage.java b/src/main/java/com/seibel/lod/objects/quadTree/QuadTreeImage.java new file mode 100644 index 000000000..ac21dc5e3 --- /dev/null +++ b/src/main/java/com/seibel/lod/objects/quadTree/QuadTreeImage.java @@ -0,0 +1,195 @@ +package com.seibel.lod.objects.quadTree; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.Timer; +@SuppressWarnings("serial") +public class QuadTreeImage extends JPanel { + private static final int PREF_W = 600; + private static final int PREF_H = PREF_W; + private List drawables = new ArrayList<>(); + + public QuadTreeImage() { + setBackground(Color.white); + } + + public void addMyDrawable(MyDrawable myDrawable) { + drawables.add(myDrawable); + repaint(); + } + + @Override + // make it bigger + public Dimension getPreferredSize() { + if (isPreferredSizeSet()) { + return super.getPreferredSize(); + } + return new Dimension(PREF_W, PREF_H); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + for (MyDrawable myDrawable : drawables) { + myDrawable.draw(g2); + } + } + + public void clearAll() { + drawables.clear(); + repaint(); + } + + private static void createAndShowGui( ) { + LodQuadTree lodQuadTree = new LodQuadTree(0,0); + for(int i = 0; i<9; i++){ + List> levelToGenerate= lodQuadTree.getLevelToGenerate(0,0,(byte) (9-i),1000,0); + boolean bw= true; + System.out.println(levelToGenerate); + for(AbstractMap.SimpleEntry levelDist : levelToGenerate){ + LodQuadTree level = levelDist.getKey(); + Color color ; + if(bw){ + color = Color.red; + bw = false; + }else{ + color = Color.blue; + bw = true; + } + + int posZ = level.getLodNodeData().startX/LodNodeData.BLOCK_WIDTH; + int posX = level.getLodNodeData().startZ/LodNodeData.BLOCK_WIDTH; + System.out.println(posX + " " + posZ); + lodQuadTree.setNodeAtLowerLevel(new LodNodeData(LodNodeData.BLOCK_LEVEL, posX, posZ, 0, 0, color,true),true); + + posZ = level.getLodNodeData().endX/LodNodeData.BLOCK_WIDTH; + posX = level.getLodNodeData().startZ/LodNodeData.BLOCK_WIDTH; + System.out.println(posX + " " + posZ); + lodQuadTree.setNodeAtLowerLevel(new LodNodeData(LodNodeData.BLOCK_LEVEL, posX, posZ, 0, 0, color,true),true); + + posZ = level.getLodNodeData().startX/LodNodeData.BLOCK_WIDTH; + posX = level.getLodNodeData().endX/LodNodeData.BLOCK_WIDTH; + System.out.println(posX + " " + posZ); + lodQuadTree.setNodeAtLowerLevel(new LodNodeData(LodNodeData.BLOCK_LEVEL, posX, posZ, 0, 0, color,true),true); + + posZ = level.getLodNodeData().endX/LodNodeData.BLOCK_WIDTH; + posX = level.getLodNodeData().endZ/LodNodeData.BLOCK_WIDTH; + System.out.println(posX + " " + posZ); + lodQuadTree.setNodeAtLowerLevel(new LodNodeData(LodNodeData.BLOCK_LEVEL, posX, posZ, 0, 0, color,true),true); + } + } + System.out.println(lodQuadTree.getNodeList(false,false,false)); + + Collection lodList = lodQuadTree.getNodeList(false,false,false); + + final List myDrawables = new ArrayList<>(); + for(LodNodeData data : lodList) { + myDrawables.add(new MyDrawable(new Rectangle2D.Double(data.startX+100, data.startZ+100, data.width, data.width), + data.color, new BasicStroke(1))); + } + + final QuadTreeImage quadTreeImage = new QuadTreeImage(); + + JFrame frame = new JFrame("DrawChit"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.getContentPane().add(quadTreeImage); + frame.pack(); + frame.setLocationByPlatform(true); + frame.setVisible(true); + + int timerDelay = 1; + new Timer(timerDelay, new ActionListener() { + private int drawCount = 0; + + @Override + public void actionPerformed(ActionEvent e) { + if (drawCount >= myDrawables.size()) { + drawCount = 0; + quadTreeImage.clearAll(); + } else { + quadTreeImage.addMyDrawable(myDrawables.get(drawCount)); + drawCount++; + } + } + }).start(); + } + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + createAndShowGui(); + } + }); + } +} + +class MyDrawable { + private Shape shape; + private Color color; + private Stroke stroke; + + public MyDrawable(Shape shape, Color color, Stroke stroke) { + this.shape = shape; + this.color = color; + this.stroke = stroke; + } + + public Shape getShape() { + return shape; + } + + public Color getColor() { + return color; + } + + public Stroke getStroke() { + return stroke; + } + + public void draw(Graphics2D g2) { + Color oldColor = g2.getColor(); + Stroke oldStroke = g2.getStroke(); + + g2.setColor(color); + g2.setStroke(stroke); + g2.draw(shape); + + g2.setColor(oldColor); + g2.setStroke(oldStroke); + } + + public void fill(Graphics2D g2) { + Color oldColor = g2.getColor(); + Stroke oldStroke = g2.getStroke(); + + g2.setColor(color); + g2.setStroke(stroke); + g2.fill(shape); + + g2.setColor(oldColor); + g2.setStroke(oldStroke); + } + +} diff --git a/src/main/java/com/seibel/lod/objects/quadTree/UsesExamples.java b/src/main/java/com/seibel/lod/objects/quadTree/UsesExamples.java index 338b27035..9d7b0d47d 100644 --- a/src/main/java/com/seibel/lod/objects/quadTree/UsesExamples.java +++ b/src/main/java/com/seibel/lod/objects/quadTree/UsesExamples.java @@ -1,6 +1,5 @@ package com.seibel.lod.objects.quadTree; -import com.seibel.lod.builders.LodBuilder; import com.seibel.lod.builders.LodNodeBuilder; import com.seibel.lod.objects.LodDimension; import net.minecraft.client.Minecraft; @@ -23,36 +22,37 @@ public class UsesExamples { * Complete the new renderer * add everything to ClientProxy for the first test * */ - +/* LodQuadTreeWorld lodWorld = new LodQuadTreeWorld(); LodQuadTreeDimension newLodDimension = new LodQuadTreeDimension(Minecraft.getInstance().world.getDimensionType(), Minecraft.getInstance().world, 10); lodWorld.addLodDimension(newLodDimension); LodQuadTreeDimension lodDimension = lodWorld.getLodDimension(Minecraft.getInstance().world.getDimensionType()); lodDimension.move(0,0); - /* - I will now generate some fake LodNodeData. This in the final implementation will be generated by a builder - this lodNodeData will be put at level 6 of the QuadTree. This LodNodeData represent a block of width - */ + + //I will now generate some fake LodNodeData. This in the final implementation will be generated by a builder + //this lodNodeData will be put at level 6 of the QuadTree. This LodNodeData represent a block of width + LodNodeData lodNodeData1 = new LodNodeData((byte) 6, 4, 4, 64, 0, new Color(0,200,0),true); LodNodeData lodNodeData2 = new LodNodeData((byte) 6, 4, 5, 64, 0, new Color(0,200,0),true); LodNodeData lodNodeData3 = new LodNodeData((byte) 6, 5, 4, 64, 0, new Color(0,0,200),true); - /* - add like this - */ + + //add like this + lodDimension.addNode(lodNodeData1); lodDimension.addNode(lodNodeData2); lodDimension.addNode(lodNodeData3); - /* - Automatic generation - */ + //Automatic generation + List nodeToGenerate = (List) lodDimension.getRegion(0,0).getNodeToGenerate(0,0, (byte) 3,1000,0); - Collections.sort(nodeToGenerate, Map.Entry.comparingByValue(); + Collections.sort(nodeToGenerate, Map.Entry.comparingByValue()); + + + // Call the builder to generate all the useful node + + */ - /* - Call the builder to generate all the useful node - */ } }