Changed various names, changed the memory calculator

This commit is contained in:
Leonardo
2021-09-29 02:16:23 +02:00
parent 328792cf4e
commit e5c1e67369
12 changed files with 404 additions and 347 deletions
+21 -33
View File
@@ -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<Direction, long[]> adjData = new HashMap<Direction, long[]>()
{{
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);
}
*/
}
}
@@ -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<HorizontalResolution> drawResolution;
// public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
public ForgeConfigSpec.EnumValue<HorizontalQuality> horizontalQuality;
public ForgeConfigSpec.EnumValue<DetailDropOff> 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> distanceGenerationMode;
public ForgeConfigSpec.EnumValue<GenerationPriority> generationPriority;
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
public ForgeConfigSpec.EnumValue<DistanceQualityDropOff> lodDistanceCalculatorType;
public ForgeConfigSpec.EnumValue<HorizontalScale> horizontalScale;
public ForgeConfigSpec.EnumValue<HorizontalQuality> 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"
@@ -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,
}
@@ -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,
}
@@ -22,26 +22,29 @@ package com.seibel.lod.enums;
* Medium <br>
* High <br>
* <br>
* 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;
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.enums;
/**
* Low <br>
* Medium <br>
* High <br>
* <br>
* 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;
}
}
@@ -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;
}
}
@@ -13,42 +13,53 @@ import com.seibel.lod.util.LodUtil;
/**
* This object holds all loaded LevelContainers
* for a given region. <Br><Br>
*
*
* <strong>Coordinate Standard: </strong><br>
* Coordinate called posX or posZ are relative LevelPos coordinates <br>
* unless stated otherwise. <br>
*
*
*
* @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
* <p>
* 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.
*
* <p>
* 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.
*
* <p>
* 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.
*
* <p>
* 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.
*
* <p>
* 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.
*
* <p>
* 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
*
* <p>
* 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()
{
@@ -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())
@@ -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)];
}
@@ -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)
+70 -1
View File
@@ -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;
}
/**