Correctly removed LodDataPoint and optimized the LevelPos use

This commit is contained in:
Leonardo
2021-08-26 13:18:18 +02:00
parent 98cbc30709
commit 74e4744ff7
18 changed files with 639 additions and 681 deletions
@@ -0,0 +1,40 @@
package com.seibel.lod.objects;
public class DataPoint
{
public static short[] createDataPoint(int height, int depth, int red, int green, int blue){
return new short[]{(short) height, (short) depth, (short) red, (short) green, (short) blue};
}
public static short getHeight(short[] dataPoint){
return dataPoint[0];
}
public static short getDepth(short[] dataPoint){
return dataPoint[1];
}
public static short getRed(short[] dataPoint){
return dataPoint[2];
}
public static short getGreen(short[] dataPoint){
return dataPoint[3];
}
public static short getBlue(short[] dataPoint){
return dataPoint[4];
}
public static short[] getHeightDepth(short[] dataPoint){
return new short[]{dataPoint[0], dataPoint[1]};
}
public static int getColor(short[] dataPoint){
int R = (dataPoint[2] << 16) & 0x00FF0000;
int G = (dataPoint[3] << 8) & 0x0000FF00;
int B = dataPoint[4] & 0x000000FF;
return 0xFF000000 | R | G | B;
}
}
@@ -0,0 +1,7 @@
package com.seibel.lod.objects.LevelPos;
public interface ImmutableLevelPos
{
public LevelPos getConvertedLevelPos(byte newDetailLevel);
public LevelPos getRegionModuleLevelPos();
}
@@ -1,15 +1,23 @@
package com.seibel.lod.objects;
package com.seibel.lod.objects.LevelPos;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.Chunk;
import java.util.Comparator;
import java.util.Map;
public class LevelPos implements Cloneable
public class LevelPos implements Cloneable, ImmutableLevelPos, MutableLevelPos
{
public final byte detailLevel;
public final int posX;
public final int posZ;
public byte detailLevel;
public int posX;
public int posZ;
public LevelPos()
{
}
public LevelPos(byte detailLevel, int posX, int posZ)
{
@@ -18,46 +26,117 @@ public class LevelPos implements Cloneable
this.detailLevel = detailLevel;
}
public LevelPos convert(byte newDetailLevel)
/**
* this operation does not change the state
*/
public LevelPos getConvertedLevelPos(byte newDetailLevel)
{
if (newDetailLevel >= detailLevel)
{
int width = 1 << (newDetailLevel - detailLevel);
return new LevelPos(
newDetailLevel,
Math.floorDiv(posX, (int) Math.pow(2, newDetailLevel - detailLevel)),
Math.floorDiv(posZ, (int) Math.pow(2, newDetailLevel - detailLevel)));
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
} else
{
int width = 1 << (detailLevel - newDetailLevel);
return new LevelPos(
newDetailLevel,
posX * (int) Math.pow(2, detailLevel - newDetailLevel),
posZ * (int) Math.pow(2, detailLevel - newDetailLevel));
posX * width,
posZ * width);
}
}
public LevelPos regionModule()
/**
* this operation does not change the state
*/
public LevelPos getRegionModuleLevelPos()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
return new LevelPos(
detailLevel,
Math.floorMod(posX, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)),
Math.floorMod(posZ, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)));
Math.floorMod(posX, width),
Math.floorMod(posZ, width));
}
/**
* this operation changes the state
*/
public void convert(byte newDetailLevel)
{
if (newDetailLevel >= detailLevel)
{
int width = 1 << (newDetailLevel - detailLevel);
detailLevel = newDetailLevel;
posX = Math.floorDiv(posX, width);
posZ = Math.floorDiv(posZ, width);
} else
{
int width = 1 << (detailLevel - newDetailLevel);
detailLevel = newDetailLevel;
posX = posX * width;
posZ = posZ * width;
}
}
/**
* this operation changes the state
*/
public void performRegionModule()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
posX = Math.floorMod(posX, width);
posX = Math.floorMod(posZ, width);
}
/**
* this operation changes the state
*/
public void applyOffset(int xOffset, int zOffset)
{
posX = posX + xOffset;
posX = posZ + zOffset;
}
/**
* this operation changes the state
*/
public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ)
{
detailLevel = newDetailLevel;
posX = newPosX;
posX = newPosZ;
}
public RegionPos getRegionPos()
{
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
return new RegionPos(
Math.floorDiv(posX, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)),
Math.floorDiv(posZ, (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel)));
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
}
@Override
public LevelPos clone()
public ChunkPos getChunkPos()
{
return new LevelPos(detailLevel, posX, posZ);
if (LodUtil.CHUNK_DETAIL_LEVEL >= detailLevel)
{
int width = 1 << (LodUtil.CHUNK_DETAIL_LEVEL - detailLevel);
return new ChunkPos(
Math.floorDiv(posX, width),
Math.floorDiv(posZ, width));
} else
{
int width = 1 << (detailLevel - LodUtil.CHUNK_DETAIL_LEVEL);
return new ChunkPos(
posX * width,
posZ * width);
}
}
/**TODO fix the region disappearing for a second*/
/**
* TODO fix the region disappearing for a second
*/
public int maxDistance(int playerPosX, int playerPosZ, int regionPosX, int regionPosZ)
{
@@ -192,7 +271,7 @@ public class LevelPos implements Cloneable
@Override
public int compare(Map.Entry<LevelPos, Integer> first, Map.Entry<LevelPos, Integer> second)
{
Integer compareResult = Integer.compare(first.getKey().detailLevel, second.getKey().detailLevel);
int compareResult = Integer.compare(first.getKey().detailLevel, second.getKey().detailLevel);
if (compareResult != 0)
{
compareResult = Integer.compare(first.getValue(), second.getValue());
@@ -201,6 +280,14 @@ public class LevelPos implements Cloneable
}
}
@Override
public LevelPos clone()
{
return new LevelPos(detailLevel, posX, posZ);
}
@Override
public int hashCode()
{
int hash = 7;
@@ -210,11 +297,12 @@ public class LevelPos implements Cloneable
return hash;
}
public boolean equals(LevelPos other)
@Override
public boolean equals(Object other)
{
return (this.detailLevel == other.detailLevel &&
this.posX == other.posX &&
this.posZ == other.posZ);
return (this.detailLevel == ((LevelPos) other).detailLevel &&
this.posX == ((LevelPos) other).posX &&
this.posZ == ((LevelPos) other).posZ);
}
@Override
@@ -0,0 +1,14 @@
package com.seibel.lod.objects.LevelPos;
import com.seibel.lod.util.LodUtil;
public interface MutableLevelPos
{
public void convert(byte newDetailLevel);
public void performRegionModule();
public void applyOffset(int xOffset, int zOffset);
public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ);
}
@@ -1,146 +0,0 @@
/*
* 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.objects;
import java.awt.*;
import java.io.Serializable;
import java.util.Objects;
import com.seibel.lod.handlers.LodDimensionFileHandler;
import com.seibel.lod.util.LodUtil;
/**
* This stores the height and color
* for a specific area in a LodChunk.
*
* @author James Seibel
* @version 8-8-2021
*/
public class LodDataPoint implements Serializable
{
/**
* This is what separates each piece of data in the toData method
*/
private static final char DATA_DELIMITER = LodDimensionFileHandler.DATA_DELIMITER;
/**
* this is how many pieces of data are exported when toData is called
*/
public static final int NUMBER_OF_DELIMITERS = 5;
/**
* a empty data point that can be used for comparisons
*/
public static final LodDataPoint EMPTY_DATA_POINT = new LodDataPoint();
/**
* highest point
*/
public short height;
/**
* lowest point
*/
public short depth;
/**
* The average color for the 6 cardinal directions
*/
public Color color;
/**
* Creates an empty LodDataPoint
*/
public LodDataPoint()
{
height = -1;
depth = -1;
color = LodUtil.COLOR_INVISIBLE;
}
public LodDataPoint(short newHeight, short newDepth, Color newColor)
{
height = newHeight;
depth = newDepth;
color = newColor;
}
public LodDataPoint(int newHeight, int newDepth, Color newColor)
{
height = (short) newHeight;
depth = (short) newDepth;
color = newColor;
}
@Override
public int hashCode()
{
return Objects.hash(this.height, this.depth, this.color);
}
public boolean equals(LodDataPoint other)
{
return (this.height == other.height
&& this.depth == other.depth
&& this.color == other.color);
}
public boolean isEmpty()
{
return this.equals(EMPTY_DATA_POINT);
}
/**
* Outputs all data in a csv format
* with the given delimiter.
* <br>
* Exports data in the form:
* <br>
* height, depth, rgb color data
*
* <br>
* example output:
* <br>
* 4, 0, 255,255,255,
*/
public String toData()
{
String s = Short.toString(height) + DATA_DELIMITER;
s += Short.toString(depth) + DATA_DELIMITER;
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
return s;
}
@Override
public String toString()
{
String s = Short.toString(height) + DATA_DELIMITER;
s += Short.toString(depth) + DATA_DELIMITER;
s += Integer.toString(color.getRed()) + DATA_DELIMITER + Integer.toString(color.getGreen()) + DATA_DELIMITER + Integer.toString(color.getBlue()) + DATA_DELIMITER;
return s;
}
}
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.handlers.LodDimensionFileHandler;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
@@ -400,7 +401,7 @@ public class LodDimension
* stored in the LOD. If an LOD already exists at the given
* coordinates it will be overwritten.
*/
public synchronized Boolean addData(LevelPos levelPos, LodDataPoint lodDataPoint, DistanceGenerationMode generationMode, boolean update, boolean dontSave)
public synchronized Boolean addData(LevelPos levelPos, short[] lodDataPoint, DistanceGenerationMode generationMode, boolean dontSave)
{
// don't continue if the region can't be saved
@@ -412,7 +413,7 @@ public class LodDimension
LodRegion region = getRegion(levelPos);
boolean nodeAdded = region.setData(levelPos, lodDataPoint, generationMode.complexity, true);
boolean nodeAdded = region.setData(levelPos, lodDataPoint, generationMode.complexity);
// only save valid LODs to disk
if (!dontSave && fileHandler != null)
{
@@ -432,20 +433,6 @@ public class LodDimension
}
/**
* Get the LodNodeData at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public LodDataPoint getData(ChunkPos chunkPos)
{
LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z);
return getData(levelPos);
}
/**
* method to get all the quadtree level that have to be generated based on the position of the player
*
@@ -474,7 +461,7 @@ public class LodDimension
if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) &&
start <= regionLevelPos.maxDistance(playerPosX, playerPosZ))
{
region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).convert(detailLevel));
region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel));
listOfData.addAll(region.getDataToGenerate(playerPosX, playerPosZ, start, end, generation, detailLevel, dataNumber));
}
}catch (Exception e){
@@ -510,7 +497,7 @@ public class LodDimension
if (end >= regionLevelPos.minDistance(playerPosX, playerPosZ) &&
start <= regionLevelPos.maxDistance(playerPosX, playerPosZ))
{
LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).convert(detailLevel));
LodRegion region = getRegion(new LevelPos(LodUtil.REGION_DETAIL_LEVEL, regionPos.x, regionPos.z).getConvertedLevelPos(detailLevel));
listOfData.addAll(region.getDataToRender(playerPosX, playerPosZ, start, end, detailLevel,zFix));
}
}catch (Exception e){
@@ -522,6 +509,19 @@ public class LodDimension
}
}
/**
* Get the LodNodeData at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public short[] getData(ChunkPos chunkPos)
{
LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z);
return getData(levelPos);
}
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
@@ -529,7 +529,7 @@ public class LodDimension
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public LodDataPoint getData(LevelPos levelPos)
public short[] getData(LevelPos levelPos)
{
if (levelPos.detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + levelPos.detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
@@ -1,16 +1,14 @@
package com.seibel.lod.objects;
import com.seibel.lod.enums.DistanceGenerationMode;
import com.seibel.lod.handlers.LodConfig;
import com.seibel.lod.objects.LevelPos.LevelPos;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import java.awt.*;
import java.io.Serializable;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
/**
* STANDARD TO FOLLOW
@@ -128,12 +126,11 @@ public class LodRegion implements Serializable
* @param levelPos
* @param dataPoint
* @param generationType
* @param update
* @return
*/
public boolean setData(LevelPos levelPos, LodDataPoint dataPoint, byte generationType, boolean update)
public boolean setData(LevelPos levelPos, short[] dataPoint, byte generationType)
{
levelPos = levelPos.regionModule();
levelPos.performRegionModule();
if ((this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] == 0) || (generationType >= this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ]))
{
@@ -141,24 +138,13 @@ public class LodRegion implements Serializable
if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++;
//add the node data
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint.color.getRed() - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (dataPoint.color.getGreen() - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (dataPoint.color.getBlue() - 128);
this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint.height;
this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint.depth;
this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint[0];
this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = dataPoint[1];
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (dataPoint[2] - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (dataPoint[3] - 128);
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (dataPoint[4] - 128);
this.generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = generationType;
this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true;
//update could be stopped and a single big update could be done at the end
LevelPos tempLevelPos = levelPos;
if (update)
{
for (byte tempLod = (byte) (levelPos.detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++)
{
tempLevelPos = tempLevelPos.convert(tempLod);
update(tempLevelPos);
}
}
return true;
} else
{
@@ -167,7 +153,7 @@ public class LodRegion implements Serializable
}
public LodDataPoint getData(ChunkPos chunkPos)
public short[] getData(ChunkPos chunkPos)
{
return getData(new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z));
}
@@ -178,7 +164,7 @@ public class LodRegion implements Serializable
* @param lod
* @return the data at the relative pos and level
*/
public LodDataPoint getData(byte lod, BlockPos blockPos)
public short[] getData(byte lod, BlockPos blockPos)
{
int posX = Math.floorMod(blockPos.getX(), (int) Math.pow(2, lod));
int posZ = Math.floorMod(blockPos.getZ(), (int) Math.pow(2, lod));
@@ -191,17 +177,15 @@ public class LodRegion implements Serializable
* @param levelPos
* @return the data at the relative pos and level
*/
public LodDataPoint getData(LevelPos levelPos)
public short[] getData(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
return new LodDataPoint(
height[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
levelPos = levelPos.getRegionModuleLevelPos();
return new short[]{height[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
new Color(colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] + 128,
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] + 128,
colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] + 128
)
);
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] + 128),
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] + 128),
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] + 128)
};
}
/**
@@ -296,8 +280,9 @@ public class LodRegion implements Serializable
}
}
} else
//now we keep exploring the top right child
{
childPos = levelPos.convert((byte) (levelPos.detailLevel - 1));
childPos = levelPos.getConvertedLevelPos((byte) (levelPos.detailLevel - 1));
if (generationType[childPos.detailLevel][childPos.posX][childPos.posZ] < generation)
{
minDistance = childPos.minDistance(playerPosX,playerPosZ,regionPosX,regionPosZ);
@@ -403,7 +388,7 @@ public class LodRegion implements Serializable
int startX;
int startZ;
for(byte bottom = (byte) (minDetailLevel + 1); bottom < levelPos.detailLevel ; bottom++){
tempLevelPos = levelPos.convert(bottom);
tempLevelPos = levelPos.getConvertedLevelPos(bottom);
startX = tempLevelPos.posX;
startZ = tempLevelPos.posZ;
sizeDiff = (int) Math.pow(2, levelPos.detailLevel - bottom);
@@ -415,7 +400,7 @@ public class LodRegion implements Serializable
}
for (byte tempLod = levelPos.detailLevel; tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++) {
tempLevelPos = levelPos.convert(tempLod);
tempLevelPos = levelPos.getConvertedLevelPos(tempLod);
update(tempLevelPos);
}
}
@@ -426,7 +411,7 @@ public class LodRegion implements Serializable
private void update(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
int numberOfChildren = 0;
/**TODO add the ability to change how the heigth and depth are determinated (for example min or max)**/
@@ -480,7 +465,7 @@ public class LodRegion implements Serializable
*/
private boolean[][] getChildren(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
boolean[][] children = new boolean[2][2];
int numberOfChild = 0;
if (minDetailLevel == levelPos.detailLevel)
@@ -512,7 +497,7 @@ public class LodRegion implements Serializable
*/
public boolean doesDataExist(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ];
}
@@ -522,7 +507,7 @@ public class LodRegion implements Serializable
*/
public DistanceGenerationMode getGenerationMode(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
DistanceGenerationMode generationMode = DistanceGenerationMode.NONE;
switch (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ])
{
@@ -558,7 +543,7 @@ public class LodRegion implements Serializable
*/
public boolean hasDataBeenGenerated(LevelPos levelPos)
{
levelPos = levelPos.regionModule();
levelPos = levelPos.getRegionModuleLevelPos();
return (generationType[levelPos.detailLevel][levelPos.posX][levelPos.posZ] != 0);
}