From e5c1e6736924285d02a38024bf3ac80838db8a26 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 29 Sep 2021 02:16:23 +0200 Subject: [PATCH] Changed various names, changed the memory calculator --- src/main/java/com/seibel/lod/Main.java | 54 ++- .../java/com/seibel/lod/config/LodConfig.java | 64 ++-- .../com/seibel/lod/enums/DetailDropOff.java | 11 +- .../lod/enums/DistanceQualityDropOff.java | 16 - .../seibel/lod/enums/HorizontalQuality.java | 27 +- .../com/seibel/lod/enums/HorizontalScale.java | 47 +++ .../com/seibel/lod/objects/LodDimension.java | 21 +- .../com/seibel/lod/objects/LodRegion.java | 340 +++++++++--------- .../com/seibel/lod/render/LodRenderer.java | 2 +- .../seibel/lod/util/DetailDistanceUtil.java | 43 ++- .../com/seibel/lod/util/LevelPosUtil.java | 55 +-- .../java/com/seibel/lod/util/LodUtil.java | 71 +++- 12 files changed, 404 insertions(+), 347 deletions(-) delete mode 100644 src/main/java/com/seibel/lod/enums/DistanceQualityDropOff.java create mode 100644 src/main/java/com/seibel/lod/enums/HorizontalScale.java diff --git a/src/main/java/com/seibel/lod/Main.java b/src/main/java/com/seibel/lod/Main.java index 9201cf7d6..0417cb22d 100644 --- a/src/main/java/com/seibel/lod/Main.java +++ b/src/main/java/com/seibel/lod/Main.java @@ -4,6 +4,9 @@ import java.util.HashMap; import java.util.Map; import com.seibel.lod.builders.lodTemplates.Box; +import com.seibel.lod.config.LodConfig; +import com.seibel.lod.enums.HorizontalQuality; +import com.seibel.lod.enums.HorizontalScale; import com.seibel.lod.util.DataPointUtil; import net.minecraft.util.Direction; @@ -12,44 +15,29 @@ public class Main { public static void main(String[] args) { - /* - try + for(byte detail = 0; detail < 13; detail++) { - @SuppressWarnings("serial") - Map adjData = new HashMap() - {{ - put(Direction.EAST, new long[]{DataPointUtil.createDataPoint(60, 30, 0, 0, 0, 0), DataPointUtil.createDataPoint(25, 10, 0, 0, 0, 0)}); - put(Direction.WEST, new long[]{DataPointUtil.createDataPoint(60, 10, 0, 0, 0, 0)}); - put(Direction.NORTH, new long[]{DataPointUtil.createDataPoint(40, 20, 0, 0, 0, 0)}); - put(Direction.SOUTH, new long[]{DataPointUtil.createDataPoint(40, 20, 0, 0, 0, 0)}); - }}; - - Box box = new Box(); - int height = 60; - int depth = 20; - - box.set(10, height - depth, 10); - box.move(0, depth, 0); - box.setAdjData(adjData); - - for(Direction direction : Box.DIRECTIONS) + byte minGenDetail = 0; + byte maxDetail = 10; + int distance; + if (detail <= minGenDetail) + distance = 0; + else if (detail >= maxDetail) + distance = 10000; + else { - int adjIndex = 0; - while (box.shouldContinue(direction, adjIndex)) + int distanceUnit = HorizontalScale.LOW.distanceUnit; + switch (HorizontalQuality.HIGH) { - System.out.println(direction.toString()); - for (int i = 0; i < 4; i++) - { - System.out.println(box.getX(direction, i) + " " + box.getY(direction, i, adjIndex) + " " + box.getZ(direction, i)); - } - adjIndex++; + case LINEAR: + ; + distance = (detail * distanceUnit); + default: + double base = HorizontalQuality.HIGH.quadraticBase; + distance = (int) (Math.pow(base, detail) * distanceUnit); } } - - } catch (Exception e) - { - e.printStackTrace(); + System.out.println(distance); } - */ } } diff --git a/src/main/java/com/seibel/lod/config/LodConfig.java b/src/main/java/com/seibel/lod/config/LodConfig.java index 1aa2dd267..02ebe6bbd 100644 --- a/src/main/java/com/seibel/lod/config/LodConfig.java +++ b/src/main/java/com/seibel/lod/config/LodConfig.java @@ -20,23 +20,13 @@ package com.seibel.lod.config; import java.nio.file.Path; import java.nio.file.Paths; +import com.seibel.lod.enums.*; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.electronwill.nightconfig.core.io.WritingMode; import com.seibel.lod.ModInfo; -import com.seibel.lod.enums.DebugMode; -import com.seibel.lod.enums.DetailDropOff; -import com.seibel.lod.enums.DistanceGenerationMode; -import com.seibel.lod.enums.DistanceQualityDropOff; -import com.seibel.lod.enums.FogDistance; -import com.seibel.lod.enums.FogDrawOverride; -import com.seibel.lod.enums.GenerationPriority; -import com.seibel.lod.enums.HorizontalQuality; -import com.seibel.lod.enums.HorizontalResolution; -import com.seibel.lod.enums.LodTemplate; -import com.seibel.lod.enums.VerticalQuality; import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -90,8 +80,7 @@ public class LodConfig public ForgeConfigSpec.EnumValue drawResolution; // public ForgeConfigSpec.EnumValue shadingMode; - - public ForgeConfigSpec.EnumValue horizontalQuality; + public ForgeConfigSpec.EnumValue detailDropOff; @@ -143,10 +132,9 @@ public class LodConfig detailDropOff = builder .comment("\n\n" + " How smooth should the detail transition for LODs be? \n" - + DetailDropOff.BY_CHUNK + ": quality is determined per-chunk (best quality option, may cause stuttering when moving)\n" - + DetailDropOff.BY_REGION_FANCY + ": quality is determined per-region (quality option)\n" - + DetailDropOff.BY_REGION_FAST + ": quality is determined per-region (performance option)\n") - .defineEnum("detailDropOff", DetailDropOff.BY_CHUNK); + + DetailDropOff.FANCY + ": quality is determined per-block (best quality option, may cause stuttering when moving)\n" + + DetailDropOff.FAST + ": quality is determined per-region (performance option)\n") + .defineEnum("detailDropOff", DetailDropOff.FANCY); drawResolution = builder .comment("\n\n" @@ -157,14 +145,7 @@ public class LodConfig + " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n" + " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n") .defineEnum("Draw resolution", HorizontalResolution.BLOCK); - - horizontalQuality = builder - .comment("\n\n" - + " This indicates how quickly LODs drop off in quality. \n" - + " " + HorizontalQuality.LOW + ": quality drops every 4 chunks. \n" - + " " + HorizontalQuality.MEDIUM + ": quality drops every 8 chunks. \n" - + " " + HorizontalQuality.HIGH + ": quality drops every 16 chunks. \n") - .defineEnum("lodDrawQuality", HorizontalQuality.MEDIUM); + lodChunkRenderDistance = builder .comment("\n\n" @@ -217,7 +198,8 @@ public class LodConfig public ForgeConfigSpec.EnumValue distanceGenerationMode; public ForgeConfigSpec.EnumValue generationPriority; public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration; - public ForgeConfigSpec.EnumValue lodDistanceCalculatorType; + public ForgeConfigSpec.EnumValue horizontalScale; + public ForgeConfigSpec.EnumValue horizontalQuality; WorldGenerator(ForgeConfigSpec.Builder builder) { @@ -240,19 +222,23 @@ public class LodConfig + " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n" + " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n") .defineEnum("Generation Resolution", HorizontalResolution.BLOCK); - - lodDistanceCalculatorType = builder - .comment("\n\n" - + " " + DistanceQualityDropOff.LINEAR + " \n" - + " with LINEAR calculator the quality of block decrease \n" - + " linearly to the distance of the player \n" - - + "\n" - + " " + DistanceQualityDropOff.QUADRATIC + " \n" - + " with QUADRATIC calculator the quality of block decrease \n" - + " quadratically to the distance of the player \n") - .defineEnum("lodDistanceComputation", DistanceQualityDropOff.LINEAR); - + + horizontalScale = builder + .comment("\n\n" + + " This indicates how quickly LODs drop off in quality. \n" + + " " + HorizontalScale.LOW + ": quality drops every " + HorizontalScale.LOW.distanceUnit/16 + " chunks. \n" + + " " + HorizontalScale.MEDIUM + ": quality drops every " + HorizontalScale.MEDIUM.distanceUnit/16 + " chunks. \n" + + " " + HorizontalScale.HIGH + ": quality drops every " + HorizontalScale.HIGH.distanceUnit/16 + " chunks. \n") + .defineEnum("horizontal scale", HorizontalScale.MEDIUM); + + horizontalQuality = builder + .comment("\n\n" + + " This indicates the exponential base of the quadratic drop-off \n" + + " " + HorizontalQuality.LINEAR + ": base " + HorizontalQuality.LINEAR.quadraticBase + ". \n" + + " " + HorizontalQuality.LOW + ": base " + HorizontalQuality.LOW.quadraticBase + ". \n" + + " " + HorizontalQuality.MEDIUM + ": base " + HorizontalQuality.MEDIUM.quadraticBase + ". \n" + + " " + HorizontalQuality.HIGH + ": base " + HorizontalQuality.HIGH.quadraticBase + ". \n") + .defineEnum("horizontal quality", HorizontalQuality.MEDIUM); generationPriority = builder .comment("\n\n" + " " + GenerationPriority.FAR_FIRST + " \n" diff --git a/src/main/java/com/seibel/lod/enums/DetailDropOff.java b/src/main/java/com/seibel/lod/enums/DetailDropOff.java index 42136e2d4..19ea60a32 100644 --- a/src/main/java/com/seibel/lod/enums/DetailDropOff.java +++ b/src/main/java/com/seibel/lod/enums/DetailDropOff.java @@ -28,11 +28,8 @@ package com.seibel.lod.enums; public enum DetailDropOff { /** quality is determined per-region, using the lowest quality that would be used in BY_CHUNK */ - BY_REGION_FAST, - - /** quality is determined per-region, using the highest quality that would be used in BY_CHUNK */ - BY_REGION_FANCY, - - /** quality is determined per-chunk (best quality option, may cause stuttering when moving) */ - BY_CHUNK, + FAST, + + /** quality is determined per-block (best quality option, may cause stuttering when moving) */ + FANCY, } diff --git a/src/main/java/com/seibel/lod/enums/DistanceQualityDropOff.java b/src/main/java/com/seibel/lod/enums/DistanceQualityDropOff.java deleted file mode 100644 index b7dacbb17..000000000 --- a/src/main/java/com/seibel/lod/enums/DistanceQualityDropOff.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.seibel.lod.enums; - -/** - * - * - * @author Leonardo Amato - * @version 8-22-2021 - */ -public enum DistanceQualityDropOff -{ - /** detail drops off linearly to the distance */ - LINEAR, - - /** detail drops off quadratically to the distance */ - QUADRATIC, -} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/HorizontalQuality.java b/src/main/java/com/seibel/lod/enums/HorizontalQuality.java index aff8d21a5..d6a23ce1a 100644 --- a/src/main/java/com/seibel/lod/enums/HorizontalQuality.java +++ b/src/main/java/com/seibel/lod/enums/HorizontalQuality.java @@ -22,26 +22,29 @@ package com.seibel.lod.enums; * Medium
* High
*
- * TODO what will the represent when it is hooked up? + * this indicate the base of the quadratic function we use for the quality drop off * * @author Leonardo Amato - * @version 9-25-2021 + * @version 9-29-2021 */ public enum HorizontalQuality { /** Lods are 2D with heightMap */ - LOW(64), - + LINEAR(1.0f), + + /** Lods are 2D with heightMap */ + LOW(1.5f), + /** Lods expand in three dimension */ - MEDIUM(128), - + MEDIUM(2f), + /** Lods expand in three dimension */ - HIGH(256); - - public int distanceUnit; - - HorizontalQuality(int distanceUnit) + HIGH(Math.E); + + public double quadraticBase; + + HorizontalQuality(double distanceUnit) { - this.distanceUnit = distanceUnit; + this.quadraticBase = distanceUnit; } } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/enums/HorizontalScale.java b/src/main/java/com/seibel/lod/enums/HorizontalScale.java new file mode 100644 index 000000000..df5951cd7 --- /dev/null +++ b/src/main/java/com/seibel/lod/enums/HorizontalScale.java @@ -0,0 +1,47 @@ +/* + * This file is part of the LOD Mod, licensed under the GNU GPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.seibel.lod.enums; + +/** + * Low
+ * Medium
+ * High
+ *
+ * this is a quality scale for the detail drop-off + * + * @author Leonardo Amato + * @version 9-25-2021 + */ +public enum HorizontalScale +{ + /** Lods are 2D with heightMap */ + LOW(64), + + /** Lods expand in three dimension */ + MEDIUM(128), + + /** Lods expand in three dimension */ + HIGH(256); + + public int distanceUnit; + + HorizontalScale(int distanceUnit) + { + this.distanceUnit = distanceUnit; + } +} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java index 2c03e764d..7332e7190 100644 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodDimension.java @@ -884,17 +884,26 @@ public class LodDimension break; case BY_REGION_FAST: }*/ - + /*return regions[x][z].getMinMemoryNeeded(template);*/ + + /*TODO add memory use calculated with the following cases + switch (LodConfig.CLIENT.graphics.detailDropOff.get()) + { + default: + case BY_BLOCK: + break; + case BY_REGION_FANCY: + break; + case BY_REGION_FAST: + }*/ int minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, x, z, halfWidth, halfWidth); int detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance); int levelToGen = DetailDistanceUtil.getLodDrawDetail(detail); - int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelToGen); int maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detail); - - int numberOfLods = size * size * maxVerticalData; - int memoryUse = numberOfLods * template.getBufferMemoryForSingleLod(maxVerticalData); - + int memoryUse = LodUtil.regionRenderingMemoryUse(x,z,template); + System.out.println(detail + " " + memoryUse + " " + template.getBufferMemoryForSingleLod(maxVerticalData)); return memoryUse; + //return memoryUse; } } diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java index 2cee404a6..a4f744196 100644 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ b/src/main/java/com/seibel/lod/objects/LodRegion.java @@ -13,42 +13,53 @@ import com.seibel.lod.util.LodUtil; /** * This object holds all loaded LevelContainers * for a given region.

- * + * * Coordinate Standard:
* Coordinate called posX or posZ are relative LevelPos coordinates
* unless stated otherwise.
- * - * + * * @author Leonardo Amato * @version 9-27-2021 */ public class LodRegion { - /** TODO what does this represent, and should it be defined here? */ + /** + * TODO what does this represent, and should it be defined here? + */ private static final byte POSSIBLE_LOD = 10; - - - /** Holds the lowest (least detailed) detail level in this region - * TODO is that correct? */ + + + /** + * Holds the lowest (least detailed) detail level in this region + * TODO is that correct? + */ private byte minDetailLevel; - - /** This holds all data for this region */ + + /** + * This holds all data for this region + */ private LevelContainer[] dataContainer; - - /** the generation mode for this region - * TODO will this ever change through a region's life? */ + + /** + * the generation mode for this region + * TODO will this ever change through a region's life? + */ private DistanceGenerationMode generationMode; - /** the vertical quality of this region */ + /** + * the vertical quality of this region + */ private VerticalQuality verticalQuality; - - /** this region's x RegionPos */ + + /** + * this region's x RegionPos + */ public final int regionPosX; - /** this region's z RegionPos */ + /** + * this region's z RegionPos + */ public final int regionPosZ; - - - - + + public LodRegion(RegionPos regionPos) { this.minDetailLevel = LodUtil.REGION_DETAIL_LEVEL; @@ -56,7 +67,7 @@ public class LodRegion this.regionPosZ = regionPos.z; dataContainer = new LevelContainer[POSSIBLE_LOD]; } - + public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality) { this.minDetailLevel = minDetailLevel; @@ -65,37 +76,37 @@ public class LodRegion this.verticalQuality = verticalQuality; this.generationMode = generationMode; dataContainer = new LevelContainer[POSSIBLE_LOD]; - - + + // Initialize all the different matrices for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++) { switch (verticalQuality) { - default: - case HEIGHTMAP: - dataContainer[lod] = new SingleLevelContainer(lod); - break; - case VOXEL: - dataContainer[lod] = new VerticalLevelContainer(lod); - break; + default: + case HEIGHTMAP: + dataContainer[lod] = new SingleLevelContainer(lod); + break; + case VOXEL: + dataContainer[lod] = new VerticalLevelContainer(lod); + break; } } } - - - + + /** * Inserts the data point into the region. - * - * TODO this will always return true unless it has + *

+ * TODO this will always return true unless it has + * * @return true if the data was added successfully */ public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data) { posX = LevelPosUtil.getRegionModule(detailLevel, posX); posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - + // The dataContainer could have null entries if the // detailLevel changes. if (this.dataContainer[detailLevel] == null) @@ -105,97 +116,97 @@ public class LodRegion else this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel); } - + this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex); - + return true; } - + /** * Get the dataPoint at the given relative position. * * @return the data at the relative pos and detail level, - * 0 if the data doesn't exist. + * 0 if the data doesn't exist. */ public long getData(byte detailLevel, int posX, int posZ, int verticalIndex) { return dataContainer[detailLevel].getData(posX, posZ, verticalIndex); } - + /** * Get the dataPoint at the given relative position. * * @return the data at the relative pos and detail level, - * 0 if the data doesn't exist. + * 0 if the data doesn't exist. */ public long getSingleData(byte detailLevel, int posX, int posZ) { return dataContainer[detailLevel].getSingleData(posX, posZ); } - - /** Clears the datapoint at the given relative position */ + + /** + * Clears the datapoint at the given relative position + */ public void clear(byte detailLevel, int posX, int posZ) { dataContainer[detailLevel].clear(posX, posZ); } - + /** - * This method will fill the posToGenerate array with all levelPos that + * This method will fill the posToGenerate array with all levelPos that * are render-able. - * + *

* TODO why don't we return the posToGenerate, it would make this easier to understand */ - public void getDataToGenerate(PosToGenerateContainer posToGenerate, - int playerBlockPosX, int playerBlockPosZ) + public void getDataToGenerate(PosToGenerateContainer posToGenerate, + int playerBlockPosX, int playerBlockPosZ) { getDataToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ); - + } - + /** - * A recursive method that fills the posToGenerate array with all levelPos that + * A recursive method that fills the posToGenerate array with all levelPos that * need to be generated. - * + *

* TODO why don't we return the posToGenerate, it would make this easier to understand */ - private void getDataToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel, - int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ) + private void getDataToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel, + int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ) { // equivalent to 2^(...) int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - + // calculate what LevelPos are in range to generate int maxDistance = LevelPosUtil.maxDistance(detailLevel, childOffsetPosX, childOffsetPosZ, playerPosX, playerPosZ, regionPosX, regionPosZ); - + // determine this child's levelPos byte childDetailLevel = (byte) (detailLevel - 1); int childPosX = childOffsetPosX * 2; int childPosZ = childOffsetPosZ * 2; - + int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - childDetailLevel); - + byte targetDetailLevel = DetailDistanceUtil.getLodGenDetail(DetailDistanceUtil.getGenerationDetailFromDistance(maxDistance)).detailLevel; if (targetDetailLevel > detailLevel) { // we have gone beyond the target Detail level // we can stop generating return; - } - else if (targetDetailLevel == detailLevel) + } else if (targetDetailLevel == detailLevel) { if (!doesDataExist(detailLevel, childOffsetPosX, childOffsetPosZ)) posToGenerate.addPosToGenerate(detailLevel, childOffsetPosX + regionPosX * size, childOffsetPosZ + regionPosZ * size); - } - else + } else { // we want at max one request per chunk (since the world generator creates chunks). // So for lod smaller than a chunk, only recurse down // the top right child - + if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL) { int ungeneratedChildren = 0; - + // make sure all children are generated to this detailLevel for (int x = 0; x <= 1; x++) { @@ -208,19 +219,18 @@ public class LodRegion } } } - + // only if all the children are correctly generated // should we go deeper if (ungeneratedChildren == 0) for (int x = 0; x <= 1; x++) for (int z = 0; z <= 1; z++) getDataToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ); - } - else + } else { // The detail Level is smaller than a chunk. // Only recurse down the top right child. - + if (DetailDistanceUtil.getLodGenDetail(childDetailLevel).detailLevel <= (childDetailLevel)) { if (!doesDataExist(childDetailLevel, childPosX, childPosZ)) @@ -231,102 +241,89 @@ public class LodRegion } } } - - + + /** - * This method will fill the posToRender array with all levelPos that + * This method will fill the posToRender array with all levelPos that * are render-able. - * + *

* TODO why don't we return the posToRender, it would make this easier to understand */ - public void getDataToRender(PosToRenderContainer posToRender, - int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel) + public void getDataToRender(PosToRenderContainer posToRender, + int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel) { getDataToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ, requireCorrectDetailLevel); } - + /** - * This method will fill the posToRender array with all levelPos that + * This method will fill the posToRender array with all levelPos that * are render-able. - * + *

* TODO why don't we return the posToRender, it would make this easier to understand * TODO this needs some more comments, James was only able to figure out part of it */ - private void getDataToRender(PosToRenderContainer posToRender, - byte detailLevel, int posX, int posZ, - int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel) + private void getDataToRender(PosToRenderContainer posToRender, + byte detailLevel, int posX, int posZ, + int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel) { // equivalent to 2^(...) int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); - + byte desiredLevel; int maxDistance; boolean stopNow = false; int minDistance; int childLevel; - - + + // calculate the LevelPos that are in range switch (LodConfig.CLIENT.graphics.detailDropOff.get()) { - default: - case BY_CHUNK: - maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ); - desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance)); - minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ); - childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance)); - stopNow = detailLevel == childLevel - 1; - break; - - case BY_REGION_FANCY: - desiredLevel = minDetailLevel; - break; - - case BY_REGION_FAST: - int playerRegionX = LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerPosX); - int playerRegionZ = LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerPosZ); - if (playerRegionX == regionPosX && playerRegionZ == regionPosZ) - { + + case FAST: + int playerRegionX = LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerPosX); + int playerRegionZ = LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerPosZ); + if (playerRegionX == regionPosX && playerRegionZ == regionPosZ) + { + maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ); + desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance)); + minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ); + childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance)); + stopNow = detailLevel == childLevel - 1; + break; + } + default: + case FANCY: maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ); desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance)); minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ); childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance)); stopNow = detailLevel == childLevel - 1; - } - else - { - maxDistance = LevelPosUtil.maxDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerRegionX * 512 + 256, playerRegionZ * 512 + 256); - desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance)); - } - break; + break; } - - + if (stopNow) { posToRender.addPosToRender(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size); - } - else if (desiredLevel > detailLevel) + } else if (desiredLevel > detailLevel) { // we have gone beyond the target Detail level // we can stop generating return; - } - else if (desiredLevel == detailLevel) + } else if (desiredLevel == detailLevel) { posToRender.addPosToRender(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size); - } - else //case where (detailLevel > desiredLevel) + } else //case where (detailLevel > desiredLevel) { int childPosX = posX * 2; int childPosZ = posZ * 2; byte childDetailLevel = (byte) (detailLevel - 1); int childrenCount = 0; - + for (int x = 0; x <= 1; x++) { for (int z = 0; z <= 1; z++) @@ -340,8 +337,8 @@ public class LodRegion } } } - - + + if (!requireCorrectDetailLevel) { // If all the four children exist go deeper @@ -350,8 +347,7 @@ public class LodRegion for (int x = 0; x <= 1; x++) for (int z = 0; z <= 1; z++) getDataToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel); - } - else + } else { posToRender.addPosToRender(detailLevel, posX + regionPosX * size, @@ -360,11 +356,11 @@ public class LodRegion } } } - - + + /** * Updates all children. - * + *

* TODO could this be renamed mergeArea? */ public void updateArea(byte detailLevel, int posX, int posZ) @@ -372,20 +368,20 @@ public class LodRegion int width; int startX; int startZ; - + // TODO what are each of these loops updating? for (byte down = (byte) (minDetailLevel + 1); down <= detailLevel; down++) { startX = LevelPosUtil.convert(detailLevel, posX, down); startZ = LevelPosUtil.convert(detailLevel, posZ, down); width = 1 << (detailLevel - down); - + for (int x = 0; x < width; x++) for (int z = 0; z < width; z++) update(down, startX + x, startZ + z); } - - + + for (byte up = (byte) (detailLevel + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++) { update(up, @@ -393,18 +389,18 @@ public class LodRegion LevelPosUtil.convert(detailLevel, posZ, up)); } } - + /** * Update the child at the given relative Pos - * + *

* TODO could this be renamed mergeChildData? */ private void update(byte detailLevel, int posX, int posZ) { dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ); } - - + + /** * Returns if data exists at the given relative Pos. */ @@ -412,16 +408,16 @@ public class LodRegion { if (detailLevel < minDetailLevel) return false; - + posX = LevelPosUtil.getRegionModule(detailLevel, posX); posZ = LevelPosUtil.getRegionModule(detailLevel, posZ); - + if (dataContainer == null || dataContainer[detailLevel] == null) return false; - + return dataContainer[detailLevel].doesItExist(posX, posZ); } - + /** * Gets the generation mode for the data point at the given relative pos. */ @@ -434,34 +430,36 @@ public class LodRegion else return DistanceGenerationMode.NONE.complexity; } - - /** Returns the lowest detail level in this region - * TODO is that right? */ + + /** + * Returns the lowest detail level in this region + * TODO is that right? + */ public byte getMinDetailLevel() { return minDetailLevel; } - + /** * Returns the LevelContainer for the detailLevel - * + * * @throws IllegalArgumentException if the detailLevel is less than minDetailLevel */ public LevelContainer getLevel(byte detailLevel) { if (detailLevel < minDetailLevel) throw new IllegalArgumentException("getLevel asked for a detail level that does not exist: minimum: [" + minDetailLevel + "] level requested: [" + detailLevel + "]"); - + return dataContainer[detailLevel]; } - + /** * Add the levelContainer to this Region, updating the minDetailLevel * if necessary. - * + * * @throws IllegalArgumentException if the LevelContainer's detailLevel - * is 2 or more detail levels lower than the - * minDetailLevel of this region. + * is 2 or more detail levels lower than the + * minDetailLevel of this region. */ public void addLevelContainer(LevelContainer levelContainer) { @@ -473,17 +471,17 @@ public class LodRegion + "only allows adding LevelContainers with a " + "detail level of [" + (minDetailLevel - 1) + "]"); } - - if (levelContainer.getDetailLevel() == minDetailLevel - 1) + + if (levelContainer.getDetailLevel() == minDetailLevel - 1) minDetailLevel = levelContainer.getDetailLevel(); - + dataContainer[levelContainer.getDetailLevel()] = levelContainer; } - + // TODO James thinks cutTree and growTree (which he renamed to match cutTree) // should have more descriptive names, to make sure the "Tree" portion isn't // confused with Minecraft trees (the plant). - + /** * Removes any dataContainers that are higher than * the given detailLevel @@ -494,11 +492,11 @@ public class LodRegion { for (byte detailLevelIndex = 0; detailLevelIndex < detailLevel; detailLevelIndex++) dataContainer[detailLevelIndex] = null; - + minDetailLevel = detailLevel; } } - + /** * Make this region more detailed to the detailLevel given. * TODO is that correct? @@ -511,20 +509,24 @@ public class LodRegion { if (dataContainer[detailLevelIndex + 1] == null) dataContainer[detailLevelIndex + 1] = new SingleLevelContainer((byte) (detailLevelIndex + 1)); - + dataContainer[detailLevelIndex] = dataContainer[detailLevelIndex + 1].expand(); } minDetailLevel = detailLevel; } } - - /** return RegionPos of this lod region */ + + /** + * return RegionPos of this lod region + */ public RegionPos getRegionPos() { return new RegionPos(regionPosX, regionPosZ); } - - /** Returns the minimum memory needed in bytes */ + + /** + * Returns the minimum memory needed in bytes + */ public int getMinMemoryNeeded(LodTemplate template) { int memory = 0; @@ -535,33 +537,35 @@ public class LodRegion } return memory; } - - /** Returns how many LODs are in this region */ + + /** + * Returns how many LODs are in this region + */ public int getNumberOfLods() { int count = 0; for (LevelContainer container : dataContainer) count += container.getMaxNumberOfLods(); - + return count; } - + public VerticalQuality getVerticalQuality() { return verticalQuality; } - + public DistanceGenerationMode getGenerationMode() { return generationMode; } - + public int getMaxVerticalData(byte detailLevel) { return dataContainer[detailLevel].getMaxVerticalData(); } - - + + @Override public String toString() { diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java index 46e761278..0b69e952b 100644 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodRenderer.java @@ -782,7 +782,7 @@ public class LodRenderer long newTime = System.currentTimeMillis(); - if(LodConfig.CLIENT.graphics.detailDropOff.get() == DetailDropOff.BY_CHUNK) + if(LodConfig.CLIENT.graphics.detailDropOff.get() == DetailDropOff.FANCY) { // check if the player has moved if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.get()) diff --git a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java index 53bafb31d..9dd57a706 100644 --- a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java +++ b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java @@ -3,6 +3,7 @@ package com.seibel.lod.util; import com.seibel.lod.config.LodConfig; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.HorizontalResolution; +import com.seibel.lod.enums.VerticalQuality; public class DetailDistanceUtil { @@ -14,8 +15,6 @@ public class DetailDistanceUtil private static int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1; private static int minDistance = 0; private static int maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 2; - private static int base = 2; - private static double logBase = Math.log(2); private static int[] maxVerticalData = { 4, @@ -49,46 +48,50 @@ public class DetailDistanceUtil public static void updateSettings(){ minGenDetail = LodConfig.CLIENT.worldGenerator.generationResolution.get().detailLevel; minDrawDetail = Math.max(LodConfig.CLIENT.graphics.drawResolution.get().detailLevel,LodConfig.CLIENT.worldGenerator.generationResolution.get().detailLevel); - maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 2; + maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 8; } public static int baseDistanceFunction(int detail) { - int distanceUnit = LodConfig.CLIENT.graphics.horizontalQuality.get().distanceUnit; if (detail <= minGenDetail) return minDistance; - if (detail == maxDetail) + if (detail >= maxDetail) return maxDistance; - if (detail == maxDetail + 1) - return maxDistance; - switch (LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.get()) - { + + int distanceUnit = LodConfig.CLIENT.worldGenerator.horizontalScale.get().distanceUnit; + switch (LodConfig.CLIENT.worldGenerator.horizontalQuality.get()){ + case LINEAR:; return (detail * distanceUnit); default: - case QUADRATIC: + double base = LodConfig.CLIENT.worldGenerator.horizontalQuality.get().quadraticBase; return (int) (Math.pow(base, detail) * distanceUnit); } } + public static int getDrawDistanceFromDetail(int detail) + { + return baseDistanceFunction(detail); + } + public static byte baseInverseFunction(int distance, int minDetail) { - int distanceUnit = LodConfig.CLIENT.graphics.horizontalQuality.get().distanceUnit; - byte detail = 0; + + int detail = 0; if (distance == 0) - detail = (byte) minDetail; - if (distance > maxDistance) - detail = (byte) (maxDetail-1); - switch (LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.get()) - { + return (byte) minDetail; + int distanceUnit = LodConfig.CLIENT.worldGenerator.horizontalScale.get().distanceUnit; + switch (LodConfig.CLIENT.worldGenerator.horizontalQuality.get()){ case LINEAR: detail = (byte) Math.floorDiv(distance, distanceUnit); break; - case QUADRATIC: + default: + double base = LodConfig.CLIENT.worldGenerator.horizontalQuality.get().quadraticBase; + double logBase = Math.log(base); detail = (byte) (Math.log(Math.floorDiv(distance, distanceUnit))/logBase); break; } - return (byte) Math.min(detail, LodUtil.REGION_DETAIL_LEVEL); + return (byte) LodUtil.clamp(minDetail, detail, maxDetail-1); } public static byte getDrawDetailFromDistance(int distance) @@ -162,6 +165,8 @@ public class DetailDistanceUtil public static int getMaxVerticalData(int detail) { + if(LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == VerticalQuality.HEIGHTMAP) + return 1; return maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)]; } diff --git a/src/main/java/com/seibel/lod/util/LevelPosUtil.java b/src/main/java/com/seibel/lod/util/LevelPosUtil.java index e8e7c059d..54e6c8e30 100644 --- a/src/main/java/com/seibel/lod/util/LevelPosUtil.java +++ b/src/main/java/com/seibel/lod/util/LevelPosUtil.java @@ -1,5 +1,7 @@ package com.seibel.lod.util; +import com.seibel.lod.config.LodConfig; + public class LevelPosUtil { public static int[] convert(int[] levelPos, byte newDetailLevel) @@ -143,19 +145,10 @@ public class LevelPosUtil public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion) { - int width = 1 << detailLevel; - - int startPosX = xRegion * 512 + posX * width; - int startPosZ = zRegion * 512 + posZ * width; - int endPosX = startPosX + width; - int endPosZ = startPosZ + width; - - int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)); - maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2))); - maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2))); - maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2))); - - return maxDistance; + int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); + int newPosX = xRegion * width + posX; + int newPosZ = zRegion * width + posZ; + return maxDistance(detailLevel, newPosX, newPosZ, playerPosX, playerPosZ); } @@ -197,38 +190,10 @@ public class LevelPosUtil public static int minDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion) { - int width = 1 << detailLevel; - - int startPosX = xRegion * 512 + posX * width; - int startPosZ = zRegion * 512 + posZ * width; - int endPosX = startPosX + width; - int endPosZ = startPosZ + width; - - boolean inXArea = playerPosX >= startPosX && playerPosX <= endPosX; - boolean inZArea = playerPosZ >= startPosZ && playerPosZ <= endPosZ; - if (inXArea && inZArea) - { - return 0; - } else if (inXArea) - { - return Math.min( - Math.abs(playerPosZ - startPosZ), - Math.abs(playerPosZ - endPosZ) - ); - } else if (inZArea) - { - return Math.min( - Math.abs(playerPosX - startPosX), - Math.abs(playerPosX - endPosX) - ); - } else - { - int minDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)); - minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2))); - minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2))); - minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2))); - return minDistance; - } + int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel); + int newPosX = xRegion * width + posX; + int newPosZ = zRegion * width + posZ; + return minDistance(detailLevel, newPosX, newPosZ, playerPosX, playerPosZ); } public static int compareDistance(int firstDistance, int secondDistance) diff --git a/src/main/java/com/seibel/lod/util/LodUtil.java b/src/main/java/com/seibel/lod/util/LodUtil.java index 19144c242..1a67a0dd1 100644 --- a/src/main/java/com/seibel/lod/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/util/LodUtil.java @@ -21,7 +21,10 @@ import java.awt.Color; import java.io.File; import java.util.HashSet; +import com.seibel.lod.config.LodConfig; +import com.seibel.lod.enums.LodTemplate; import com.seibel.lod.objects.LodDimension; +import com.seibel.lod.objects.LodRegion; import com.seibel.lod.objects.RegionPos; import com.seibel.lod.wrappers.MinecraftWrapper; @@ -318,7 +321,73 @@ public class LodUtil { return Math.min(max, Math.max(value, min)); } - + + /** + * This methods return the number of lods that are going to be rendered in a region in the worst case + * @param regionPosX x region position to check + * @param regionPosZ z region position to check + * @return number of lods in the region + */ + public static int regionRenderingMemoryUse(int regionPosX, int regionPosZ, LodTemplate template) + { + int xRegionSign = (int) Math.signum(regionPosX); + int zRegionSign = (int) Math.signum(regionPosZ); + + //we first find the center of the circle which is one of the following X position in the center region + /* + X - X - X + | | + X X X + | | + X - X - X + */ + int circleCenterX = 256 + 256*xRegionSign; + int circleCenterZ = 256 + 256*zRegionSign; + + + int innerRadius; + int outerRadius; + int size; + int count; + int minDistance; + int maxDistance; + int memoryUse = 0; + int number = 0; + for(byte detailLevel = BLOCK_DETAIL_LEVEL; detailLevel <= REGION_DETAIL_LEVEL; detailLevel++) + { + //We find now the inner and outer detail of this area + innerRadius = DetailDistanceUtil.getDrawDistanceFromDetail(detailLevel); + outerRadius = DetailDistanceUtil.getDrawDistanceFromDetail(detailLevel + 1); + + //we skip if the region does not intersect the two circles. + minDistance = LevelPosUtil.minDistance(REGION_DETAIL_LEVEL, regionPosX, regionPosZ, circleCenterX, circleCenterZ); + maxDistance = LevelPosUtil.maxDistance(REGION_DETAIL_LEVEL, regionPosX, regionPosZ, circleCenterX, circleCenterZ); + if (innerRadius > maxDistance || minDistance > outerRadius) + continue; + + //we proceed to count all the position in the region that fall between these two circle + size = 1 << (REGION_DETAIL_LEVEL - detailLevel); + count = 0; + for (int x = 0; x < size; x++) + { + for (int z = 0; z < size; z++) + { + minDistance = LevelPosUtil.minDistance(detailLevel, x, z, circleCenterX, circleCenterZ, regionPosX, regionPosZ); + if (innerRadius < minDistance && minDistance < outerRadius) + count++; + } + } + + //we multiply the data with the max vertical data of this detail level + int maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel); + + number += count; + count *= maxVerticalData; + memoryUse += template.getBufferMemoryForSingleLod(maxVerticalData) * count; + } + System.out.println(number); + return memoryUse; + } /**