QuadTree version, still doesn't work
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
package com.seibel.lod.builders;
|
||||
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDataPoint;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.objects.quadTree.LodNodeData;
|
||||
import com.seibel.lod.objects.quadTree.LodQuadTreeWorld;
|
||||
import kaptainwutax.biomeutils.source.BiomeSource;
|
||||
import kaptainwutax.biomeutils.source.OverworldBiomeSource;
|
||||
import kaptainwutax.mcutils.state.Dimension;
|
||||
import kaptainwutax.mcutils.version.MCVersion;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Iterator;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class LodNodeBuilder {
|
||||
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor();
|
||||
private long seed;
|
||||
private DimensionType dimension;
|
||||
|
||||
/** Default size of any LOD regions we use */
|
||||
public int regionWidth = 5;
|
||||
|
||||
|
||||
/** fast biome calculator */
|
||||
private BiomeSource biomeSource;
|
||||
//Biome biome=biomeSource.getBiome(x,y,z); // here y is always 0 no matter what you pass
|
||||
|
||||
public LodNodeBuilder(){
|
||||
|
||||
}
|
||||
public setApproxGenerator(long seed){
|
||||
//Dimension.OVERWORLD;
|
||||
//Dimension.END;
|
||||
//Dimension.NETHER;
|
||||
biomeSource = BiomeSource.of(Dimension.OVERWORLD ,MCVersion.v1_16_4, seed);
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(List<LodNodeData> dataList){
|
||||
Thread thread = new Thread(() ->{
|
||||
for(LodNodeData data : dataList){
|
||||
|
||||
}
|
||||
});
|
||||
thread.setPriority(4);
|
||||
lodGenThreadPool.execute(thread);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunk chunk, LodWorld lodWorld, IWorld world)
|
||||
{
|
||||
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
// given a valid chunk object
|
||||
if (chunk == null)
|
||||
return;
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
DimensionType dim = world.getDimensionType();
|
||||
|
||||
LodChunk lod = generateLodFromChunk(chunk, config);
|
||||
|
||||
LodDimension lodDim;
|
||||
|
||||
if (lodWorld.getLodDimension(dim) == null)
|
||||
{
|
||||
lodDim = new LodDimension(dim, lodWorld, regionWidth);
|
||||
lodWorld.addLodDimension(lodDim);
|
||||
}
|
||||
else
|
||||
{
|
||||
lodDim = lodWorld.getLodDimension(dim);
|
||||
}
|
||||
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
catch(IllegalArgumentException | NullPointerException e)
|
||||
{
|
||||
// if the world changes while LODs are being generated
|
||||
// they will throw errors as they try to access things that no longer
|
||||
// exist.
|
||||
}
|
||||
});
|
||||
lodGenThreadPool.execute(thread);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a LodChunk for a chunk in the given world.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* thrown if either the chunk or world is null.
|
||||
*/
|
||||
public LodChunk generateLodFromChunk(IChunk chunk) throws IllegalArgumentException
|
||||
{
|
||||
return generateLodFromChunk(chunk, new LodBuilderConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LodChunk for a chunk in the given world.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* thrown if either the chunk or world is null.
|
||||
*/
|
||||
public LodChunk generateLodFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException
|
||||
{
|
||||
if(chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
|
||||
|
||||
LodDetail detail = LodConfig.CLIENT.lodDetail.get();
|
||||
LodDataPoint[][] dataPoints = new LodDataPoint[detail.lengthCount][detail.lengthCount];
|
||||
|
||||
for(int i = 0; i < detail.lengthCount * detail.lengthCount; i++)
|
||||
{
|
||||
int startX = detail.startX[i];
|
||||
int startZ = detail.startZ[i];
|
||||
int endX = detail.endX[i];
|
||||
int endZ = detail.endZ[i];
|
||||
|
||||
Color color;
|
||||
|
||||
color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ);
|
||||
|
||||
|
||||
short height;
|
||||
short depth;
|
||||
|
||||
if (!config.useHeightmap)
|
||||
{
|
||||
height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = determineHeightPoint(chunk.getHeightmap(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ);
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
int x = i / detail.lengthCount;
|
||||
int z = i % detail.lengthCount;
|
||||
|
||||
dataPoints[x][z] = new LodDataPoint(height, depth, color);
|
||||
}
|
||||
|
||||
return new LodChunk(chunk.getPos(), dataPoints, detail);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package com.seibel.lod.handlers;
|
||||
|
||||
import com.seibel.lod.objects.quadTree.LodNodeData;
|
||||
import com.seibel.lod.objects.quadTree.LodQuadTree;
|
||||
import com.seibel.lod.objects.quadTree.LodQuadTreeDimension;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class LodQuadTreeDimensionFileHandler {
|
||||
/** This is what separates each piece of data */
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
|
||||
|
||||
private LodQuadTreeDimension loadedDimension = null;
|
||||
public long regionLastWriteTime[][];
|
||||
|
||||
private File dimensionDataSaveFolder;
|
||||
|
||||
/** lod */
|
||||
private final String FILE_NAME_PREFIX = "lod";
|
||||
/** .txt */
|
||||
private final String FILE_EXTENSION = ".txt";
|
||||
|
||||
/** This is the file version currently accepted by this
|
||||
* file handler, older versions (smaller numbers) will be deleted and overwritten,
|
||||
* newer versions (larger numbers) will be ignored and won't be read. */
|
||||
public static final int LOD_SAVE_FILE_VERSION = 2;
|
||||
|
||||
/** This is the string written before the file version */
|
||||
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
|
||||
|
||||
/** Allow saving asynchronously, but never try to save multiple regions
|
||||
* at a time */
|
||||
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor();
|
||||
|
||||
|
||||
public LodQuadTreeDimensionFileHandler(File newSaveFolder, LodQuadTreeDimension newLoadedDimension)
|
||||
{
|
||||
if (newSaveFolder == null)
|
||||
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
|
||||
|
||||
dimensionDataSaveFolder = newSaveFolder;
|
||||
|
||||
loadedDimension = newLoadedDimension;
|
||||
// these two variable are used in sync with the LodDimension
|
||||
regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()];
|
||||
for(int i = 0; i < loadedDimension.getWidth(); i++)
|
||||
for(int j = 0; j < loadedDimension.getWidth(); j++)
|
||||
regionLastWriteTime[i][j] = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// read from file //
|
||||
//================//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the LodQuadTree region at the given coordinates.
|
||||
* (null if the file doesn't exist)
|
||||
*/
|
||||
public LodQuadTree loadRegionFromFile(int regionX, int regionZ)
|
||||
{
|
||||
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ);
|
||||
|
||||
File f = new File(fileName);
|
||||
|
||||
if (!f.exists())
|
||||
{
|
||||
// there wasn't a file, don't
|
||||
// return anything
|
||||
return null;
|
||||
}
|
||||
|
||||
List<LodNodeData> dataList = new ArrayList<>();
|
||||
try
|
||||
{
|
||||
BufferedReader br = new BufferedReader(new FileReader(f));
|
||||
String s = br.readLine();
|
||||
int fileVersion = -1;
|
||||
if(s != null && !s.isEmpty())
|
||||
{
|
||||
// try to get the file version
|
||||
try
|
||||
{
|
||||
fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim());
|
||||
}
|
||||
catch(NumberFormatException | StringIndexOutOfBoundsException e)
|
||||
{
|
||||
// this file doesn't have a version
|
||||
// keep the version as -1
|
||||
fileVersion = -1;
|
||||
}
|
||||
|
||||
// check if this file can be read by this file handler
|
||||
if(fileVersion < LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is an older version,
|
||||
// close the reader and delete the file.
|
||||
br.close();
|
||||
f.delete();
|
||||
ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
|
||||
", version requested: " + LOD_SAVE_FILE_VERSION +
|
||||
" File was been deleted.");
|
||||
|
||||
return null;
|
||||
}
|
||||
else if(fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// close the reader and ignore the file, we don't
|
||||
// want to accidently delete anything the user may want.
|
||||
br.close();
|
||||
ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
|
||||
", version requested: " + LOD_SAVE_FILE_VERSION +
|
||||
" this region will not be written to in order to protect the newer file.");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// there is no data in this file
|
||||
br.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// this file is a readable version, begin reading the file
|
||||
s = br.readLine();
|
||||
|
||||
while(s != null && !s.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
dataList.add(new LodNodeData(s));
|
||||
}
|
||||
catch(IllegalArgumentException e)
|
||||
{
|
||||
// we were unable to create this chunk
|
||||
// for whatever reason.
|
||||
// skip to the next chunk
|
||||
ClientProxy.LOGGER.warn(e.getMessage());
|
||||
}
|
||||
|
||||
s = br.readLine();
|
||||
}
|
||||
br.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// the buffered reader encountered a
|
||||
// problem reading the file
|
||||
return null;
|
||||
}
|
||||
return new LodQuadTree(dataList,regionX, regionZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// Save to File //
|
||||
//==============//
|
||||
|
||||
/**
|
||||
* Save all dirty regions in this LodDimension to file.
|
||||
*/
|
||||
public void saveDirtyRegionsToFileAsync()
|
||||
{
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsThread);
|
||||
}
|
||||
|
||||
private Thread saveDirtyRegionsThread = new Thread(() ->
|
||||
{
|
||||
for(int i = 0; i < loadedDimension.getWidth(); i++)
|
||||
{
|
||||
for(int j = 0; j < loadedDimension.getWidth(); j++)
|
||||
{
|
||||
if(loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null)
|
||||
{
|
||||
saveRegionToDisk(loadedDimension.regions[i][j]);
|
||||
loadedDimension.isRegionDirty[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a specific region to disk.<br>
|
||||
* Note: <br>
|
||||
* 1. If a file already exists for a newer version
|
||||
* the file won't be written.<br>
|
||||
* 2. This will save to the LodDimension that this
|
||||
* handler is associated with.
|
||||
*/
|
||||
private void saveRegionToDisk(LodQuadTree region)
|
||||
{
|
||||
// convert chunk coordinates to region
|
||||
// coordinates
|
||||
int x = region.getLodNodeData().posX;
|
||||
int z = region.getLodNodeData().posX;
|
||||
|
||||
File f = new File(getFileNameAndPathForRegion(x, z));
|
||||
|
||||
try
|
||||
{
|
||||
// make sure the file and folder exists
|
||||
if (!f.exists())
|
||||
{
|
||||
// the file doesn't exist,
|
||||
// create it and the folder if need be
|
||||
if(!f.getParentFile().exists())
|
||||
f.getParentFile().mkdirs();
|
||||
f.createNewFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
// the file exists, make sure it
|
||||
// is the correct version.
|
||||
// (to make sure we don't overwrite a newer
|
||||
// version file if it exists)
|
||||
|
||||
BufferedReader br = new BufferedReader(new FileReader(f));
|
||||
String s = br.readLine();
|
||||
int fileVersion = LOD_SAVE_FILE_VERSION;
|
||||
|
||||
if(s != null && !s.isEmpty())
|
||||
{
|
||||
// try to get the file version
|
||||
try
|
||||
{
|
||||
fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim());
|
||||
}
|
||||
catch(NumberFormatException | StringIndexOutOfBoundsException e)
|
||||
{
|
||||
// this file doesn't have a correctly formated version
|
||||
// just overwrite the file
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
|
||||
// check if this file can be written to by the file handler
|
||||
if(fileVersion <= LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// we are good to continue and overwrite the old file
|
||||
}
|
||||
else //if(fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// don't write anything, we don't want to accidently
|
||||
// delete anything the user may want.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FileWriter fw = new FileWriter(f);
|
||||
|
||||
// add the version of this file
|
||||
fw.write(LOD_FILE_VERSION_PREFIX + " " + LOD_SAVE_FILE_VERSION + "\n");
|
||||
|
||||
// add each LodChunk to the file
|
||||
for(LodNodeData lodNodeData : Collections.unmodifiableList(region.getNodeList(false, true, true))) {
|
||||
fw.write(lodNodeData.toData() + "\n");
|
||||
lodNodeData.dirty = false;
|
||||
}
|
||||
fw.close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
ClientProxy.LOGGER.error("LOD file write error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
* Returns null if this object isn't ready to read and write. <br><br>
|
||||
*
|
||||
* example: "lod.FULL.0.0.txt"
|
||||
*/
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ)
|
||||
{
|
||||
try
|
||||
{
|
||||
// saveFolder is something like
|
||||
// ".\Super Flat\DIM-1\data"
|
||||
// or
|
||||
// ".\Super Flat\data"
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
|
||||
FILE_NAME_PREFIX + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package com.seibel.lod.objects.quadTree;
|
||||
|
||||
import com.seibel.lod.handlers.LodQuadTreeDimensionFileHandler;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
public class LodNodeData {
|
||||
/** This is what separates each piece of data in the toData method */
|
||||
private static final char DATA_DELIMITER = LodQuadTreeDimensionFileHandler.DATA_DELIMITER;
|
||||
|
||||
|
||||
|
||||
/** this is how many pieces of data are exported when toData is called */
|
||||
public static final int NUMBER_OF_DELIMITERS = 9;
|
||||
|
||||
private static final Color INVISIBLE = new Color(0,0,0,0);
|
||||
|
||||
//level height goes from 0 to 9 with 0 the deepest (block size) and 9 the highest (region size)
|
||||
public final byte level;
|
||||
public static final byte REGION_LEVEL = 9; //at level 9 we reach the dimension of a single region
|
||||
public static final byte CHUNK_LEVEL = 4; //at level 4 we reach the dimension of a single chunk
|
||||
public static final byte BLOCK_LEVEL = 0; //at level 0 we reach the dimension of a single block
|
||||
|
||||
//indicate the width in block of this node (goes from 1 to 512)
|
||||
public final short width;
|
||||
public static final short REGION_WIDTH = 512; //at level 9 we reach the dimension of a single region
|
||||
public static final short CHUNK_WIDTH = 16; //at level 4 we reach the dimension of a single chunk
|
||||
public static final short BLOCK_WIDTH = 1; //at level 0 we reach the dimension of a single block
|
||||
|
||||
//this 2 values indicate the position of the LOD in the relative Level
|
||||
//this will be useful in the generation process
|
||||
public final int posX;
|
||||
public final int posZ;
|
||||
|
||||
//these 4 value indicate the corner of the LOD block
|
||||
//they can be named SW, SE, NW, NE as the cardinal direction.
|
||||
//the start values should always be smaller than the end values.
|
||||
//All this value could be calculated from level and levelWidth
|
||||
//so they could be removed and replaced with just a getter
|
||||
public final int startX;
|
||||
public final int startZ;
|
||||
public final int endX;
|
||||
public final int endZ;
|
||||
//these 2 value indicate the center of the LodNode in real coordinate. This
|
||||
//can be used to calculate the distance from the player
|
||||
public final int centerX;
|
||||
public final int centerZ;
|
||||
|
||||
/** highest point */
|
||||
public short height;
|
||||
|
||||
/** lowest point */
|
||||
public short depth;
|
||||
|
||||
/** The average color for the 6 cardinal directions */
|
||||
public Color color;
|
||||
|
||||
public boolean real;
|
||||
public boolean voidNode;
|
||||
//if dirty is true, then this node have unsaved changes
|
||||
public boolean dirty;
|
||||
|
||||
/**
|
||||
* Creates and empty LodDataPoint
|
||||
* This LodDataPoint only contains the position data
|
||||
* @param level of the node
|
||||
* @param posX position x in the level
|
||||
* @param posZ posizion z in the level
|
||||
*/
|
||||
public LodNodeData(byte level, int posX, int posZ){
|
||||
this.level = level;
|
||||
this.posX = posX;
|
||||
this.posZ = posZ;
|
||||
width = (short) Math.pow(2, level);
|
||||
startX = posX * width;
|
||||
startZ = posZ * width;
|
||||
endX = startX + width - 1;
|
||||
endZ = startZ + width - 1;
|
||||
centerX = startX + width/2;
|
||||
centerZ = startZ + width/2;
|
||||
height = -1;
|
||||
depth = -1;
|
||||
color = INVISIBLE;
|
||||
real = false;
|
||||
dirty = true;
|
||||
voidNode = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a LodNodeData
|
||||
* @param level level of this
|
||||
* @param posX
|
||||
* @param posZ
|
||||
* @param height
|
||||
* @param depth
|
||||
* @param color
|
||||
* @param real
|
||||
*/
|
||||
public LodNodeData(byte level, int posX, int posZ, short height, short depth, Color color, boolean real){
|
||||
this.level = level;
|
||||
this.posX = posX;
|
||||
this.posZ = posZ;
|
||||
width = (short) Math.pow(2, level);
|
||||
startX = posX * width;
|
||||
startZ = posZ * width;
|
||||
endX = startX + width - 1;
|
||||
endZ = startZ + width - 1;
|
||||
centerX = startX + width/2;
|
||||
centerZ = startZ + width/2;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
this.color = color;
|
||||
this.real = real;
|
||||
dirty = true;
|
||||
voidNode = false;
|
||||
}
|
||||
|
||||
public LodNodeData(byte level, int posX, int posZ, int height, int depth, Color color, boolean real) {
|
||||
this(level, posX, posZ, (short) height,(short) depth, color, real);
|
||||
}
|
||||
|
||||
public LodNodeData(String data)
|
||||
{
|
||||
int index = 0;
|
||||
int lastIndex = 0;
|
||||
|
||||
index = data.indexOf(DATA_DELIMITER, 0);
|
||||
this.level = (byte) Integer.parseInt(data.substring(0,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
this.posX = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
this.posZ = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
this.height = (short) Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
this.depth = (short) Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int r = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int g = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int b = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex+1);
|
||||
int a = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
this.color = new Color(r,g,b,a);
|
||||
|
||||
int val = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
this.real = (val == 1);
|
||||
width = (short) Math.pow(2, level);
|
||||
|
||||
val = Integer.parseInt(data.substring(lastIndex+1,index));
|
||||
this.voidNode = (val == 1);
|
||||
startX = posX * width;
|
||||
startZ = posZ * width;
|
||||
endX = startX + width - 1;
|
||||
endZ = startZ + width - 1;
|
||||
centerX = startX + width/2;
|
||||
centerZ = startZ + width/2;
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
public void update(LodNodeData lodNodeData){
|
||||
this.height = lodNodeData.height;
|
||||
this.depth = lodNodeData.depth;
|
||||
this.color = lodNodeData.color;
|
||||
this.real = lodNodeData.real;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void combineData(List<LodNodeData> dataList){
|
||||
if(dataList.isEmpty()){
|
||||
height = -1;
|
||||
depth = -1;
|
||||
color = INVISIBLE;
|
||||
}else {
|
||||
short height = (short) dataList.stream().mapToInt(x -> (int) x.height).min().getAsInt();
|
||||
short depth = (short) dataList.stream().mapToInt(x -> (int) x.depth).max().getAsInt();
|
||||
height = height;
|
||||
depth = depth;
|
||||
color = dataList.get(0).color;
|
||||
real = dataList.stream().filter(x -> x.real).count() == 4;
|
||||
voidNode = dataList.stream().filter(x -> !x.voidNode).count() == 0;
|
||||
}
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int hashCode(){
|
||||
return Objects.hash(this.real, this.level, this.posX, this.posZ, this.color, this.real, this.voidNode);
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(LodNodeData other){
|
||||
return (this.real == other.real
|
||||
&& this.level == other.level
|
||||
&& this.posX == other.posX
|
||||
&& this.posZ == other.posZ
|
||||
&& this.color.equals(other.color)
|
||||
&& this.real == other.real
|
||||
&& this.voidNode == other.voidNode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Outputs all data in a csv format
|
||||
*/
|
||||
public String toData(){
|
||||
String s = Integer.toString(level) + DATA_DELIMITER
|
||||
+ posX + DATA_DELIMITER
|
||||
+ posZ + DATA_DELIMITER
|
||||
+ Integer.toString(height) + DATA_DELIMITER
|
||||
+ Integer.toString(depth) + DATA_DELIMITER
|
||||
+ color.getRed() + DATA_DELIMITER
|
||||
+ color.getGreen() + DATA_DELIMITER
|
||||
+ color.getBlue() + DATA_DELIMITER
|
||||
+ color.getAlpha() + DATA_DELIMITER;
|
||||
int val = real ? 1 : 0;
|
||||
s += val + DATA_DELIMITER;
|
||||
val = voidNode ? 1 : 0;
|
||||
s += val + DATA_DELIMITER;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return this.toData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
package com.seibel.lod.objects.quadTree;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This object contains all data useful to render LodBlock in a region (32x32 chunk o 512x512 block)
|
||||
* for every node it contains the border of the block, the size, the position at it's level, the color, the height and the depth.
|
||||
*/
|
||||
public class LodQuadTree {
|
||||
//notes
|
||||
//The term node correspond to a LodQuadTree object
|
||||
|
||||
|
||||
/*
|
||||
Example on how it will be rendered (the number correspond to the level in the LodNodeData)
|
||||
.___.___._______._______________.
|
||||
|6|6| 7 | | |
|
||||
|6|6|___| 8 | |
|
||||
| 7 | 7 | | |
|
||||
|___|___|_______| 9 |
|
||||
| | | |
|
||||
| 8 | 8 | |
|
||||
| | | |
|
||||
|_______|_______|_______________|
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| 9 | 9 |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
|_______________|_______________|
|
||||
*/
|
||||
//data useful to render
|
||||
//if children are present then lodNodeData should be a combination of the lodData of the child. This can be
|
||||
//turned off by deselecting the recursive update in all update method.
|
||||
private final LodNodeData lodNodeData;
|
||||
/*
|
||||
.____.____.
|
||||
| NW | NE | |
|
||||
|____|____| Z
|
||||
| SW | SE | |
|
||||
|____|____| V
|
||||
-----X---->
|
||||
|
||||
North - negative z
|
||||
South - positive z
|
||||
West - negative x
|
||||
east - positive x
|
||||
*/
|
||||
|
||||
|
||||
//level completed is true if and only if all child are not null
|
||||
private boolean nodeFull;
|
||||
private boolean nodeEmpty;
|
||||
|
||||
//the four child based on the four diagonal cardinal direction
|
||||
//the first index is for N and S and the second index is for W and S
|
||||
//children should always be null for level 0.
|
||||
private final LodQuadTree[][] children;
|
||||
|
||||
//parent should always be null for level 9, and always not null for other levels.
|
||||
private final LodQuadTree parent;
|
||||
|
||||
/**
|
||||
* Constructor for level 0 without LodNodeData (region level constructor)
|
||||
*
|
||||
* @param regionX indicate the x region position of the node
|
||||
* @param regionZ indicate the z region position of the node
|
||||
*/
|
||||
//maybe the use of useLevelCoordinate could be changed. I could use a builder to do all this work.
|
||||
public LodQuadTree(int regionX, int regionZ) {
|
||||
this(null, new LodNodeData(LodNodeData.REGION_LEVEL, regionX, regionZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for generic level without LodNodeData
|
||||
*
|
||||
* @param parent parent of this node
|
||||
* @param level level of this note
|
||||
* @param posX position x in the level
|
||||
* @param posZ position z in the level
|
||||
*/
|
||||
public LodQuadTree(LodQuadTree parent, byte level, int posX, int posZ) {
|
||||
this(parent, new LodNodeData(level, posX, posZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for generic level via the LodNodeData
|
||||
*
|
||||
* @param lodNodeData object containing all the information of this node
|
||||
*/
|
||||
public LodQuadTree(LodQuadTree parent, LodNodeData lodNodeData) {
|
||||
this.parent = parent;
|
||||
this.lodNodeData = lodNodeData;
|
||||
this.children = new LodQuadTree[2][2];
|
||||
this.nodeEmpty = true;
|
||||
this.nodeFull = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor using a dataList
|
||||
*
|
||||
* @param dataList list of LodNodeData to put in this LodQuadTree
|
||||
* @param regionX x region coordinate
|
||||
* @param regionZ z region coordinate
|
||||
*/
|
||||
public LodQuadTree(List<LodNodeData> dataList, int regionX, int regionZ) {
|
||||
this(null, new LodNodeData(LodNodeData.REGION_LEVEL, regionX, regionZ));
|
||||
this.setNodesAtLowerLevel(dataList, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param dataList list of data to put in the node
|
||||
* @param updateHigherLevel will update the color and height of higher level only if true
|
||||
*/
|
||||
public void setNodesAtLowerLevel(List<LodNodeData> dataList, boolean updateHigherLevel) {
|
||||
for (LodNodeData lodNodeData : dataList) {
|
||||
//this is slow, you could set update to false and use an only top down update method.
|
||||
this.setNodeAtLowerLevel(lodNodeData, updateHigherLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newLodNodeData data to put in the node
|
||||
* @param updateHigherLevel will update the color and height of higher level only if true
|
||||
* @return true only if the QuadTree has been changed
|
||||
*/
|
||||
public boolean setNodeAtLowerLevel(LodNodeData newLodNodeData, boolean updateHigherLevel) {
|
||||
//check if we try to introduce a level that is higher or equal than the current one
|
||||
byte targetLevel = newLodNodeData.level;
|
||||
byte currentLevel = lodNodeData.level;
|
||||
if (targetLevel < currentLevel) {
|
||||
int posX = newLodNodeData.posX;
|
||||
int posZ = newLodNodeData.posZ;
|
||||
short widthRatio = (short) (lodNodeData.width / newLodNodeData.width);
|
||||
int NS = (posX / widthRatio) % lodNodeData.posX;
|
||||
int WE = (posZ / widthRatio) % lodNodeData.posZ;
|
||||
if (getChild(NS, WE) == null) {
|
||||
setChild(NS, WE);
|
||||
}
|
||||
LodQuadTree child = getChild(NS, WE);
|
||||
if (!newLodNodeData.real && child.isNodeReal()) {
|
||||
return false;
|
||||
} else {
|
||||
if (targetLevel == currentLevel - 1) {
|
||||
child.setLodNodeData(lodNodeData, updateHigherLevel);
|
||||
return true;
|
||||
} else {
|
||||
return child.setNodeAtLowerLevel(lodNodeData, updateHigherLevel);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public LodQuadTree getChild(int NS, int WE) {
|
||||
return children[NS][WE];
|
||||
}
|
||||
|
||||
/**
|
||||
* setChild will put a child with given data in the given position
|
||||
*
|
||||
* @param newLodNodeData data to put in the child
|
||||
* @param NS North-South position
|
||||
* @param WE West-East position
|
||||
*/
|
||||
public void setChild(LodNodeData newLodNodeData, int NS, int WE) {
|
||||
if (newLodNodeData.level == lodNodeData.level - 1) {
|
||||
children[NS][WE] = new LodQuadTree(this, lodNodeData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setChild will put a child with given data in the given position
|
||||
*
|
||||
* @param newLodNodeData data to put in the child
|
||||
*/
|
||||
public void setChild(LodNodeData newLodNodeData) {
|
||||
if (newLodNodeData.level == lodNodeData.level - 1) {
|
||||
int NS = newLodNodeData.posX % lodNodeData.posX;
|
||||
int WE = newLodNodeData.posZ % lodNodeData.posZ;
|
||||
children[NS][WE] = new LodQuadTree(this, lodNodeData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setChild will put a child in the given position
|
||||
*
|
||||
* @param NS North-South position
|
||||
* @param WE West-East position
|
||||
*/
|
||||
public void setChild(int NS, int WE) {
|
||||
int childX = lodNodeData.posX * 2 + WE;
|
||||
int childZ = lodNodeData.posZ * 2 + NS;
|
||||
children[NS][WE] = new LodQuadTree(this, (byte) (lodNodeData.level - 1), childX, childZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update level update the level data such as levelFull and lodNodeData.
|
||||
*
|
||||
* @param recursiveUpdate if recursive is true the update will rise up to the level 0
|
||||
*/
|
||||
private void updateLevel(boolean recursiveUpdate) {
|
||||
boolean isFull = true;
|
||||
boolean isEmpty = true;
|
||||
List<LodNodeData> dataList = new ArrayList<>();
|
||||
for (int NS = 0; NS <= 1; NS++) {
|
||||
for (int WE = 0; WE <= 1; WE++) {
|
||||
if (children[NS][WE] != null) {
|
||||
dataList.add(children[NS][WE].getLodNodeData());
|
||||
isEmpty = false;
|
||||
} else {
|
||||
isFull = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
nodeFull = isFull;
|
||||
nodeEmpty = isEmpty;
|
||||
lodNodeData.combineData(dataList);
|
||||
if (lodNodeData.level > 0 && recursiveUpdate) {
|
||||
this.parent.updateLevel(recursiveUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* method to get certain nodes from the LodQuadTree
|
||||
*
|
||||
* @param getOnlyReal if true it will return only real nodes
|
||||
* @param getOnlyDirty if true it will return only dirty nodes
|
||||
* @param getOnlyLeaf if true it will return only leaf nodes
|
||||
* @return list of nodes
|
||||
*/
|
||||
public List<LodNodeData> getNodeList(boolean getOnlyReal, boolean getOnlyDirty, boolean getOnlyLeaf) {
|
||||
List<LodNodeData> nodeList = new ArrayList<>();
|
||||
if (!isThereAnyChild()) {
|
||||
if (!(getOnlyReal && !lodNodeData.dirty)
|
||||
&& !(getOnlyReal && !lodNodeData.real)) {
|
||||
nodeList.add(lodNodeData);
|
||||
}
|
||||
} else {
|
||||
if (!getOnlyLeaf
|
||||
&& !(getOnlyDirty && !lodNodeData.dirty)
|
||||
&& !(getOnlyReal && !lodNodeData.real)) {
|
||||
nodeList.add(lodNodeData);
|
||||
}
|
||||
for (int NS = 0; NS <= 1; NS++) {
|
||||
for (int WE = 0; WE <= 1; WE++) {
|
||||
LodQuadTree child = children[NS][WE];
|
||||
if (child != null) {
|
||||
nodeList.addAll(child.getNodeList(getOnlyReal, getOnlyDirty, getOnlyLeaf));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* method to get certain nodes from the LodQuadTree
|
||||
* @return list of nodes
|
||||
*/
|
||||
public List<LodNodeData> getNodeToRender(int x, int z, byte targetLevel, int maxDistance, int minDistance){
|
||||
int distance = (int) Math.sqrt(Math.pow(x + lodNodeData.centerX,2) + Math.pow(z + lodNodeData.centerZ,2));
|
||||
List<LodNodeData> nodeList = new ArrayList<>();
|
||||
if(distance > maxDistance || distance < minDistance || targetLevel > lodNodeData.level) {
|
||||
return nodeList;
|
||||
}
|
||||
if(targetLevel == lodNodeData.level || !isThereAnyChild()){
|
||||
if(!lodNodeData.voidNode){
|
||||
nodeList.add(lodNodeData);
|
||||
return nodeList;
|
||||
}else{
|
||||
return nodeList;
|
||||
}
|
||||
} else {
|
||||
for (int NS = 0; NS <= 1; NS++) {
|
||||
for (int WE = 0; WE <= 1; WE++) {
|
||||
LodQuadTree child = children[NS][WE];
|
||||
if (child != null) {
|
||||
nodeList.addAll(child.getNodeToRender(x,z,targetLevel,maxDistance,minDistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* method to get certain nodes from the LodQuadTree
|
||||
* @return list of nodes
|
||||
*/
|
||||
public List<AbstractMap.SimpleEntry<LodNodeData,Integer>> getNodeToGenerate(int x, int z, byte targetLevel, int maxDistance, int minDistance){
|
||||
int distance = (int) Math.sqrt(Math.pow(x + lodNodeData.centerX,2) + Math.pow(z + lodNodeData.centerZ,2));
|
||||
List<AbstractMap.SimpleEntry<LodNodeData,Integer>> nodeList = new ArrayList<>();
|
||||
if(distance > maxDistance || distance < minDistance || targetLevel > lodNodeData.level) {
|
||||
return nodeList;
|
||||
}
|
||||
if(targetLevel == lodNodeData.level){
|
||||
return nodeList;
|
||||
} else {
|
||||
if(!isThereAnyChild()){
|
||||
for (int NS = 0; NS <= 1; NS++) {
|
||||
for (int WE = 0; WE <= 1; WE++) {
|
||||
LodQuadTree child = children[NS][WE];
|
||||
if (child != null) {
|
||||
nodeList.addAll(child.getNodeToGenerate(x,z,targetLevel,maxDistance,minDistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
nodeList.add( new AbstractMap.SimpleEntry<>(lodNodeData,distance));
|
||||
}
|
||||
}
|
||||
return nodeList;
|
||||
}
|
||||
/**
|
||||
* simple getter for lodNodeData
|
||||
*
|
||||
* @return lodNodeData
|
||||
*/
|
||||
public LodNodeData getLodNodeData() {
|
||||
return lodNodeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for lodNodeData, to maintain a correct relationship between level this method force update on all parent
|
||||
*
|
||||
* @param newLodNodeData data to set
|
||||
* @param updateHigherLevel if true it will update all the upper levels.
|
||||
*/
|
||||
public void setLodNodeData(LodNodeData newLodNodeData, boolean updateHigherLevel) {
|
||||
this.lodNodeData.update(lodNodeData);
|
||||
//a recursive update is necessary to change higher level
|
||||
if (parent != null && updateHigherLevel) parent.updateLevel(true);
|
||||
}
|
||||
|
||||
public boolean isNodeFull() {
|
||||
return nodeFull;
|
||||
}
|
||||
|
||||
public boolean isThereAnyChild() {
|
||||
return nodeEmpty;
|
||||
}
|
||||
|
||||
public boolean isNodeReal() {
|
||||
return lodNodeData.real;
|
||||
}
|
||||
|
||||
public boolean isRenderable() {
|
||||
return (lodNodeData != null);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
EXAMPLES OF USES
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,418 @@
|
||||
package com.seibel.lod.objects.quadTree;
|
||||
|
||||
import com.seibel.lod.handlers.LodQuadTreeDimensionFileHandler;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class LodQuadTreeDimension {
|
||||
public final DimensionType dimension;
|
||||
|
||||
private volatile int width;
|
||||
private volatile int halfWidth;
|
||||
public long seed;
|
||||
|
||||
public volatile LodQuadTree regions[][];
|
||||
public volatile boolean isRegionDirty[][];
|
||||
|
||||
private int centerX;
|
||||
private int centerZ;
|
||||
|
||||
private LodQuadTreeDimensionFileHandler fileHandler;
|
||||
|
||||
|
||||
public LodQuadTreeDimension(DimensionType newDimension, LodWorld lodWorld, int newMaxWidth)
|
||||
{
|
||||
dimension = newDimension;
|
||||
width = newMaxWidth;
|
||||
|
||||
try
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
File saveDir;
|
||||
if(mc.isIntegratedServerRunning())
|
||||
{
|
||||
// local world
|
||||
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension);
|
||||
seed = serverWorld.getSeed();
|
||||
// provider needs a separate variable to prevent
|
||||
// the compiler from complaining
|
||||
ServerChunkProvider provider = serverWorld.getChunkProvider();
|
||||
saveDir = new File(provider.getSavedData().folder.getCanonicalFile().getPath() + File.separatorChar + "lod");
|
||||
}
|
||||
else
|
||||
{
|
||||
// connected to server
|
||||
|
||||
saveDir = new File(mc.gameDir.getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.world));
|
||||
}
|
||||
|
||||
fileHandler = new LodQuadTreeDimensionFileHandler(saveDir, this);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
// the file handler wasn't able to be created
|
||||
// we won't be able to read or write any files
|
||||
}
|
||||
|
||||
|
||||
regions = new LodQuadTree[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
|
||||
// populate isRegionDirty
|
||||
for(int i = 0; i < width; i++)
|
||||
for(int j = 0; j < width; j++)
|
||||
isRegionDirty[i][j] = false;
|
||||
|
||||
centerX = 0;
|
||||
centerZ = 0;
|
||||
|
||||
halfWidth = (int)Math.floor(width / 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move the center of this LodDimension and move all owned
|
||||
* regions over by the given x and z offset.
|
||||
*/
|
||||
public void move(int xOffset, int zOffset)
|
||||
{
|
||||
// if the x or z offset is equal to or greater than
|
||||
// the total size, just delete the current data
|
||||
// and update the centerX and/or centerZ
|
||||
if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width)
|
||||
{
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
for(int z = 0; z < width; z++)
|
||||
{
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// update the new center
|
||||
centerX += xOffset;
|
||||
centerZ += zOffset;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// X
|
||||
if(xOffset > 0)
|
||||
{
|
||||
// move everything over to the left (as the center moves to the right)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
for(int z = 0; z < width; z++)
|
||||
{
|
||||
if(x + xOffset < width)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything over to the right (as the center moves to the left)
|
||||
for(int x = width - 1; x >= 0; x--)
|
||||
{
|
||||
for(int z = 0; z < width; z++)
|
||||
{
|
||||
if(x + xOffset >= 0)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Z
|
||||
if(zOffset > 0)
|
||||
{
|
||||
// move everything up (as the center moves down)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
for(int z = 0; z < width; z++)
|
||||
{
|
||||
if(z + zOffset < width)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything down (as the center moves up)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
for(int z = width - 1; z >= 0; z--)
|
||||
{
|
||||
if(z + zOffset >= 0)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// update the new center
|
||||
centerX += xOffset;
|
||||
centerZ += zOffset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the region at the given X and Z
|
||||
* <br>
|
||||
* Returns null if the region doesn't exist
|
||||
* or is outside the loaded area.
|
||||
*/
|
||||
public LodQuadTree getRegion(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - centerX) + halfWidth;
|
||||
int zIndex = (regionZ - centerZ) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(regionX, regionZ))
|
||||
// out of range
|
||||
return null;
|
||||
|
||||
if (regions[xIndex][zIndex] == null)
|
||||
{
|
||||
regions[xIndex][zIndex] = getRegionFromFile(regionX, regionZ);
|
||||
if (regions[xIndex][zIndex] == null)
|
||||
{
|
||||
regions[xIndex][zIndex] = new LodQuadTree(regionX, regionZ);
|
||||
}
|
||||
}
|
||||
|
||||
return regions[xIndex][zIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the LodRegion at the location of newRegion with newRegion.
|
||||
* @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension.
|
||||
*/
|
||||
public void setRegion(LodQuadTree newRegion) throws ArrayIndexOutOfBoundsException
|
||||
{
|
||||
int xIndex = (newRegion.getLodNodeData().posX - centerX) + halfWidth;
|
||||
int zIndex = (centerZ - newRegion.getLodNodeData().posZ) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(newRegion.getLodNodeData().posX, newRegion.getLodNodeData().posZ))
|
||||
// out of range
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
|
||||
regions[xIndex][zIndex] = newRegion;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add the given LOD to this dimension at the coordinate
|
||||
* stored in the LOD. If an LOD already exists at the given
|
||||
* coordinates it will be overwritten.
|
||||
*/
|
||||
public void addNode(LodNodeData lodNodeData)
|
||||
{
|
||||
RegionPos pos = new RegionPos(
|
||||
lodNodeData.posX / lodNodeData.width,
|
||||
lodNodeData.posZ / lodNodeData.width
|
||||
);
|
||||
|
||||
// don't continue if the region can't be saved
|
||||
if (!regionIsInRange(pos.x, pos.z))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LodQuadTree region = getRegion(pos.x, pos.z);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
// if no region exists, create it
|
||||
region = new LodQuadTree(pos.x, pos.z);
|
||||
setRegion(region);
|
||||
}
|
||||
|
||||
region.setNodeAtLowerLevel(lodNodeData, true);
|
||||
|
||||
// don't save empty place holders to disk
|
||||
if (!lodNodeData.real && fileHandler != null)
|
||||
{
|
||||
// mark the region as dirty so it will be saved to disk
|
||||
int xIndex = (pos.x - centerX) + halfWidth;
|
||||
int zIndex = (pos.z - centerZ) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
fileHandler.saveDirtyRegionsToFileAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LodNodeData at the given X and Z position in the level
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public LodNodeData getLodFromCoordinates(int posX, int posZ, byte level)
|
||||
{
|
||||
/*TODO */
|
||||
return null;
|
||||
/*
|
||||
RegionPos pos = LodUtil.convertChunkPosToRegionPos(new ChunkPos(chunkX, chunkZ));
|
||||
|
||||
LodQuadTree region = getRegion(pos.x, pos.z);
|
||||
|
||||
if(region == null)
|
||||
return null;
|
||||
|
||||
return region.getNode(chunkX, chunkZ);
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* method to get all the nodes that have to be rendered based on the position of the player
|
||||
* @return list of nodes
|
||||
*/
|
||||
public List getNodeToRender(int x, int z){
|
||||
//BASIC IDEA
|
||||
//Get all the node to render from every region
|
||||
//combine them toghether
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* method to get all the nodes that have to be generated based on the position of the player
|
||||
* @return list of nodes
|
||||
*/
|
||||
public List getNodeToGenerate(int x, int z){
|
||||
//BASIC IDEA
|
||||
//Get all the node to generate from every region
|
||||
//combine them toghether
|
||||
//sort them based on the distance to player
|
||||
//this way
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the region at the given X and Z coordinates from the
|
||||
* RegionFileHandler.
|
||||
*/
|
||||
public LodQuadTree getRegionFromFile(int regionX, int regionZ)
|
||||
{
|
||||
if (fileHandler != null)
|
||||
return fileHandler.loadRegionFromFile(regionX, regionZ);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the region at the given X and Z coordinates
|
||||
* is within the loaded range.
|
||||
*/
|
||||
public boolean regionIsInRange(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - centerX) + halfWidth;
|
||||
int zIndex = (regionZ - centerZ) + halfWidth;
|
||||
|
||||
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public int getCenterX()
|
||||
{
|
||||
return centerX;
|
||||
}
|
||||
|
||||
public int getCenterZ()
|
||||
{
|
||||
return centerZ;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns how many non-null LodChunks
|
||||
* are stored in this LodDimension.
|
||||
*/
|
||||
public int getNumberOfLods()
|
||||
{
|
||||
int numbLods = 0;
|
||||
for (LodQuadTree[] regions : regions)
|
||||
{
|
||||
if(regions == null)
|
||||
continue;
|
||||
|
||||
for (LodQuadTree region : regions)
|
||||
{
|
||||
if(region == null)
|
||||
continue;
|
||||
|
||||
numbLods= region.getNodeList(false,false,true).size();
|
||||
}
|
||||
}
|
||||
|
||||
return numbLods;
|
||||
}
|
||||
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setRegionWidth(int newWidth)
|
||||
{
|
||||
width = newWidth;
|
||||
halfWidth = (int)Math.floor(width / 2);
|
||||
|
||||
regions = new LodQuadTree[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
|
||||
// populate isRegionDirty
|
||||
for(int i = 0; i < width; i++)
|
||||
for(int j = 0; j < width; j++)
|
||||
isRegionDirty[i][j] = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = "";
|
||||
|
||||
s += "dim: " + dimension.toString() + "\t";
|
||||
s += "(" + centerX + "," + centerZ + ")";
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.seibel.lod.objects.quadTree;
|
||||
|
||||
import net.minecraft.world.DimensionType;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
public class LodQuadTreeWorld {
|
||||
|
||||
private String worldName;
|
||||
|
||||
private Map<DimensionType, LodQuadTreeDimension> lodDimensions;
|
||||
/**
|
||||
* If true then the LOD world is setup and ready to use
|
||||
*/
|
||||
private boolean isWorldLoaded = false;
|
||||
|
||||
public static final String NO_WORLD_LOADED = "No world loaded";
|
||||
|
||||
public LodQuadTreeWorld() {
|
||||
worldName = NO_WORLD_LOADED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the LodWorld with the given newWorldName. <br>
|
||||
* This should be done whenever loading a new world.
|
||||
* @param newWorldName name of the world
|
||||
*/
|
||||
public void selectWorld(String newWorldName) {
|
||||
if (newWorldName.isEmpty()) {
|
||||
deselectWorld();
|
||||
return;
|
||||
}
|
||||
|
||||
if (worldName.equals(newWorldName))
|
||||
// don't recreate everything if we
|
||||
// didn't actually change worlds
|
||||
return;
|
||||
|
||||
worldName = newWorldName;
|
||||
lodDimensions = new Hashtable<>();
|
||||
isWorldLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the worldName to "No world loaded"
|
||||
* and clear the lodDimensions Map. <br>
|
||||
* This should be done whenever unloaded a world.
|
||||
*/
|
||||
public void deselectWorld() {
|
||||
worldName = NO_WORLD_LOADED;
|
||||
lodDimensions = null;
|
||||
isWorldLoaded = false;
|
||||
}
|
||||
|
||||
|
||||
public void addLodDimension(LodQuadTreeDimension newStorage) {
|
||||
if (lodDimensions == null)
|
||||
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
|
||||
|
||||
lodDimensions.put(newStorage.dimension, newStorage);
|
||||
}
|
||||
|
||||
public LodQuadTreeDimension getLodDimension(DimensionType dimension) {
|
||||
if (lodDimensions.get(dimension) == null) {
|
||||
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
|
||||
}
|
||||
return lodDimensions.get(dimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the max width in regions that each LodDimension
|
||||
* should use.
|
||||
*/
|
||||
public void resizeDimensionRegionWidth(int newWidth) {
|
||||
if (lodDimensions == null)
|
||||
throw new IllegalStateException("LodWorld hasn't been given a world yet.");
|
||||
|
||||
for (DimensionType key : lodDimensions.keySet())
|
||||
lodDimensions.get(key).setRegionWidth(newWidth);
|
||||
}
|
||||
|
||||
|
||||
public boolean getIsWorldLoaded() {
|
||||
return isWorldLoaded;
|
||||
}
|
||||
|
||||
public String getWorldName() {
|
||||
return worldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "World name: " + worldName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.seibel.lod.objects.quadTree;
|
||||
|
||||
import com.seibel.lod.builders.LodBuilder;
|
||||
import com.seibel.lod.builders.LodNodeBuilder;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.DimensionType;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class UsesExamples {
|
||||
public static void main(String[] args){
|
||||
//THIS CODE DOESN'T WORK AT THE MOMENT
|
||||
/**TODO
|
||||
* Complete all the new Lod objects
|
||||
* Complete the getNodeToGenerate in LodQuadTreeDimension
|
||||
* Complete the getNodeToRender in LodQuadTreeDimension
|
||||
* Complete the node builder
|
||||
* Complete the new renderer
|
||||
* add everything to ClientProxy for the first test
|
||||
* */
|
||||
|
||||
LodQuadTreeWorld lodWorld = new LodQuadTreeWorld();
|
||||
LodQuadTreeDimension newLodDimension = new LodQuadTreeDimension(Minecraft.getInstance().world.getDimensionType(), Minecraft.getInstance().world, 10);
|
||||
lodWorld.addLodDimension(newLodDimension);
|
||||
|
||||
LodQuadTreeDimension lodDimension = lodWorld.getLodDimension(Minecraft.getInstance().world.getDimensionType());
|
||||
lodDimension.move(0,0);
|
||||
/*
|
||||
I will now generate some fake LodNodeData. This in the final implementation will be generated by a builder
|
||||
this lodNodeData will be put at level 6 of the QuadTree. This LodNodeData represent a block of width
|
||||
*/
|
||||
LodNodeData lodNodeData1 = new LodNodeData((byte) 6, 4, 4, 64, 0, new Color(0,200,0),true);
|
||||
LodNodeData lodNodeData2 = new LodNodeData((byte) 6, 4, 5, 64, 0, new Color(0,200,0),true);
|
||||
LodNodeData lodNodeData3 = new LodNodeData((byte) 6, 5, 4, 64, 0, new Color(0,0,200),true);
|
||||
/*
|
||||
add like this
|
||||
*/
|
||||
lodDimension.addNode(lodNodeData1);
|
||||
lodDimension.addNode(lodNodeData2);
|
||||
lodDimension.addNode(lodNodeData3);
|
||||
/*
|
||||
Automatic generation
|
||||
|
||||
*/
|
||||
List nodeToGenerate = (List) lodDimension.getRegion(0,0).getNodeToGenerate(0,0, (byte) 3,1000,0);
|
||||
Collections.sort(nodeToGenerate, Map.Entry.<LodNodeData, Integer>comparingByValue();
|
||||
|
||||
/*
|
||||
Call the builder to generate all the useful node
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user