From c45f0b241406ad84cb55eb82697a78b4ff463739 Mon Sep 17 00:00:00 2001 From: Morippi Date: Tue, 14 Dec 2021 13:46:10 +0100 Subject: [PATCH] completed the merge method --- .../lod/core/dataFormat/ColorFormat.java | 31 ++ .../lod/core/dataFormat/DataMergeUtil.java | 29 ++ .../lod/core/dataFormat/LightFormat.java | 10 +- .../objects/lod/VerticalLevelContainer.java | 314 +++++++++++++++++- 4 files changed, 361 insertions(+), 23 deletions(-) create mode 100644 src/main/java/com/seibel/lod/core/dataFormat/DataMergeUtil.java diff --git a/src/main/java/com/seibel/lod/core/dataFormat/ColorFormat.java b/src/main/java/com/seibel/lod/core/dataFormat/ColorFormat.java index 778963409..2fd875246 100644 --- a/src/main/java/com/seibel/lod/core/dataFormat/ColorFormat.java +++ b/src/main/java/com/seibel/lod/core/dataFormat/ColorFormat.java @@ -11,4 +11,35 @@ public class ColorFormat public final static long RED_MASK = 0b1111_1111; public final static long GREEN_MASK = 0b1111_1111; public final static long BLUE_MASK = 0b1111_1111; + + public static int createColorData(int alpha, int red, int green, int blue) + { + int colorData = 0; + colorData += (alpha & ALPHA_MASK) << ALPHA_SHIFT; + colorData += (red & RED_MASK) << RED_SHIFT; + colorData += (green & GREEN_MASK) << GREEN_SHIFT; + colorData += (blue & BLUE_MASK) << BLUE_SHIFT; + + return colorData; + } + + public static short getAlpha(long dataPoint) + { + return (short) ((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK); + } + + public static short getRed(long dataPoint) + { + return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK); + } + + public static short getGreen(long dataPoint) + { + return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK); + } + + public static short getBlue(long dataPoint) + { + return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK); + } } diff --git a/src/main/java/com/seibel/lod/core/dataFormat/DataMergeUtil.java b/src/main/java/com/seibel/lod/core/dataFormat/DataMergeUtil.java new file mode 100644 index 000000000..44f1ead7f --- /dev/null +++ b/src/main/java/com/seibel/lod/core/dataFormat/DataMergeUtil.java @@ -0,0 +1,29 @@ +package com.seibel.lod.core.dataFormat; + +public class DataMergeUtil +{ + public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize) + { + start *= packetSize; + length *= packetSize; + arraySize *= packetSize; + for (int i = 0; i < arraySize - start; i++) + { + array[start + i] = array[start + length + i]; + //remove comment to not leave garbage at the end + //array[start + packetSize + i] = 0; + } + } + + public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize) + { + start *= packetSize; + length *= packetSize; + arraySize *= packetSize; + for (int i = arraySize - start - 1; i >= 0; i--) + { + array[start + length + i] = array[start + i]; + array[start + i] = 0; + } + } +} diff --git a/src/main/java/com/seibel/lod/core/dataFormat/LightFormat.java b/src/main/java/com/seibel/lod/core/dataFormat/LightFormat.java index 389bf0227..158f53e2a 100644 --- a/src/main/java/com/seibel/lod/core/dataFormat/LightFormat.java +++ b/src/main/java/com/seibel/lod/core/dataFormat/LightFormat.java @@ -12,27 +12,27 @@ public class LightFormat public final static byte SKY_LIGHT_MASK = 0b1111; - public byte formatLightAsByte(byte skyLight, byte blockLight) + public static byte formatLightAsByte(byte skyLight, byte blockLight) { return (byte) (((skyLight & SKY_LIGHT_MASK) << (BYTE_SKY_LIGHT_SHIFT + 4)) | ((blockLight & BLOCK_LIGHT_MASK) << (BYTE_BLOCK_LIGHT_SHIFT + 4))); } - public int formatLightAsInt(byte skyLight, byte blockLight) + public static int formatLightAsInt(byte skyLight, byte blockLight) { return ((skyLight & SKY_LIGHT_MASK) << INT_SKY_LIGHT_SHIFT) | ((blockLight & BLOCK_LIGHT_MASK) << INT_BLOCK_LIGHT_SHIFT); } - public int convertByteToIntFormat(byte lights) + public static int convertByteToIntFormat(byte lights) { return formatLightAsInt((byte) ((lights >>> BYTE_SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK), (byte) ((lights >>> BYTE_BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK)); } - public byte getSkyLight(byte lights) + public static byte getSkyLight(byte lights) { return (byte) ((lights >>> BYTE_SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK); } - public byte getBlockLight(byte lights) + public static byte getBlockLight(byte lights) { return (byte) ((lights >>> BYTE_BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK); } diff --git a/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java b/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java index 1ef16065d..c3beca58a 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/VerticalLevelContainer.java @@ -19,14 +19,13 @@ package com.seibel.lod.core.objects.lod; -import com.seibel.lod.core.dataFormat.PositionDataFormat; -import com.seibel.lod.core.dataFormat.VerticalDataFormat; +import com.seibel.lod.core.dataFormat.*; import com.seibel.lod.core.enums.config.DistanceGenerationMode; import com.seibel.lod.core.util.*; import com.seibel.lod.core.wrapperInterfaces.IVersionConstants; /** - * + * * @author Leonardo Amato * @version ?? */ @@ -81,6 +80,7 @@ public class VerticalLevelContainer implements LevelContainer dataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = data; return true; } + @Override public boolean addVerticalData(long[] data, int posX, int posZ) { @@ -101,6 +101,46 @@ public class VerticalLevelContainer implements LevelContainer return dataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex]; } + public short getPositionData(int posX, int posZ) + { + return positionDataContainer[posX * size + posZ]; + } + + public int getVerticalData(int posX, int posZ, int verticalIndex) + { + return verticalDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex]; + } + + public int getColorData(int posX, int posZ, int verticalIndex) + { + return colorDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex]; + } + + public byte getLightData(int posX, int posZ, int verticalIndex) + { + return lightDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex]; + } + + public void setPositionData(short positionData, int posX, int posZ) + { + positionDataContainer[posX * size + posZ] = positionData; + } + + public void setVerticalData(int verticalData, int posX, int posZ, int verticalIndex) + { + verticalDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = verticalData; + } + + public void setColorData(int colorData, int posX, int posZ, int verticalIndex) + { + colorDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = colorData; + } + + public void setLightData(byte lightData, int posX, int posZ, int verticalIndex) + { + lightDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = lightData; + } + @Override public long getSingleData(int posX, int posZ) { @@ -255,22 +295,23 @@ public class VerticalLevelContainer implements LevelContainer * This method merge column of multiple data together * @return one column of correctly parsed data */ - public void mergeAndAddData(int posZ, int posX, short[] inputPositionDataToMerge, int[] inputVerticalData, int[] inputColorData, int[] inputLightData, byte inputDetailLevel, int inputVerticalSize) + public void mergeAndAddData(int posZ, int posX, short[] inputPositionDataToMerge, int[] inputVerticalData, int[] inputColorData, byte[] inputLightData, byte inputDetailLevel, int inputVerticalSize) { mergeAndAddData(0, inputPositionDataToMerge.length, posZ, posX, inputPositionDataToMerge, inputVerticalData, inputColorData, inputLightData, inputDetailLevel, inputVerticalSize); } + /** * This method merge column of multiple data together * @return one column of correctly parsed data */ - public void mergeAndAddData(int sliceStart, int sliceEnd, int posZ, int posX, short[] inputPositionDataToMerge, int[] inputVerticalData, int[] inputColorData, int[] inputLightData, byte inputDetailLevel, int inputVerticalSize) + public void mergeAndAddData(int sliceStart, int sliceEnd, int posZ, int posX, short[] inputPositionData, int[] inputVerticalData, int[] inputColorData, byte[] inputLightData, byte inputDetailLevel, int inputVerticalSize) { //STEP 1// //We initially reset this position of the data container positionDataContainer[posX * size + posZ] = PositionDataFormat.EMPTY_DATA; - for(int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++) + for (int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++) { verticalDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = VerticalDataFormat.EMPTY_LOD; lightDataContainer[posX * size * verticalSize + posZ * verticalSize + verticalIndex] = 0; @@ -289,9 +330,9 @@ public class VerticalLevelContainer implements LevelContainer short tempPositionData; //we combine every position in the slice of the input //I THINK YOU CAN SEE HOW TO USE THE SLICE FROM HERE - for(int index = sliceStart; index <=sliceEnd; index++) + for (int positionIndex = sliceStart; positionIndex <= sliceEnd; positionIndex++) { - tempPositionData = inputPositionDataToMerge[index]; + tempPositionData = inputPositionData[positionIndex]; genMode = (byte) Math.min(genMode, PositionDataFormat.getGenerationMode(tempPositionData)); correctLight &= PositionDataFormat.getFlag(tempPositionData); allVoid &= PositionDataFormat.isVoid(tempPositionData); @@ -300,19 +341,20 @@ public class VerticalLevelContainer implements LevelContainer //Case 1: should never happen but we use this just in case //if all the data is empty (maybe a bug) then we simply return - if(allEmpty) + if (allEmpty) { return; } //Case 2: if all the data is void //if all the data is empty (maybe a bug) then we simply return - if(allVoid) + if (allVoid) { positionDataContainer[posX * size + posZ] = PositionDataFormat.createVoidPositionData(genMode); return; } + //Case 3: data is non void and non empty, we continue positionDataContainer[posX * size + posZ] = PositionDataFormat.createPositionData(0, correctLight, genMode); @@ -334,9 +376,127 @@ public class VerticalLevelContainer implements LevelContainer */ int inputSize = 1 << inputDetailLevel; - // I'll disable the ThreadMap array for the initial testing ThreadMapUtil.getHeightAndDepth(inputVerticalSize * 2 * 4) + // I'll disable the ThreadMap array for the initial testing //ThreadMapUtil.getHeightAndDepth(inputVerticalSize * 2 * 4) short[] heightAndDepth = new short[inputVerticalSize * 2 * 4]; + int tempVerticalData; + short depth; + short height; + int count = 0; + int i; + int ii; + //We collect the indexes of the data, ordered by the depth + for (int positionIndex = 0; positionIndex < size; positionIndex++) + { + tempPositionData = inputPositionData[positionIndex]; + if (!PositionDataFormat.doesItExist(tempPositionData) || PositionDataFormat.isVoid(tempPositionData)) + continue; + for (int verticalIndex = 0; verticalIndex < inputVerticalSize; verticalIndex++) + { + tempVerticalData = inputVerticalData[positionIndex * inputVerticalSize + verticalIndex]; + if (VerticalDataFormat.doesItExist(tempVerticalData)) + { + depth = VerticalDataFormat.getDepth(tempVerticalData); + height = VerticalDataFormat.getDepth(tempVerticalData); + + int botPos = -1; + int topPos = -1; + //values fall in between and possibly require extension of array + boolean botExtend = false; + boolean topExtend = false; + for (i = 0; i < count; i++) + { + if (depth <= heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1]) + { + botPos = i; + break; + } + else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth > heightAndDepth[(i + 1) * 2]) || i + 1 == count)) + { + botPos = i; + botExtend = true; + break; + } + } + for (i = 0; i < count; i++) + { + if (height <= heightAndDepth[i * 2] && height >= heightAndDepth[i * 2 + 1]) + { + topPos = i; + break; + } + else if (height < heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count)) + { + topPos = i; + topExtend = true; + break; + } + } + if (topPos == -1) + { + if (botPos == -1) + { + //whole block falls above + DataMergeUtil.extendArray(heightAndDepth, 2, 0, 1, count); + heightAndDepth[0] = height; + heightAndDepth[1] = depth; + count++; + } + else if (!botExtend) + { + //only top falls above extending it there, while bottom is inside existing + DataMergeUtil.shrinkArray(heightAndDepth, 2, 0, botPos, count); + heightAndDepth[0] = height; + count -= botPos; + } + else + { + //top falls between some blocks, extending those as well + DataMergeUtil.shrinkArray(heightAndDepth, 2, 0, botPos, count); + heightAndDepth[0] = height; + heightAndDepth[1] = depth; + count -= botPos; + } + } + else if (!topExtend) + { + if (!botExtend) + //both top and bottom are within some exiting blocks, possibly merging them + heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1]; + else + //top falls between some blocks, extending it there + heightAndDepth[topPos * 2 + 1] = depth; + DataMergeUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); + count -= botPos - topPos; + } + else + { + if (!botExtend) + { + //only top is within some exiting block, extending it + topPos++; //to make it easier + heightAndDepth[topPos * 2] = height; + heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1]; + DataMergeUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); + count -= botPos - topPos; + } + else + { + //both top and bottom are outside existing blocks + DataMergeUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count); + count -= botPos - topPos; + DataMergeUtil.extendArray(heightAndDepth, 2, topPos + 1, 1, count); + count++; + heightAndDepth[topPos * 2 + 2] = height; + heightAndDepth[topPos * 2 + 3] = depth; + } + } + } + else + break; + } + } + //STEP 4// @@ -360,14 +520,132 @@ public class VerticalLevelContainer implements LevelContainer this way we reduce from verticalSize 3 to verticalSize 2 */ + //we limit the vertical portion to maxVerticalData + int j = 0; + while (count > verticalSize) + { + ii = DataPointUtil.WORLD_HEIGHT - DataPointUtil.VERTICAL_OFFSET; + for (i = 0; i < count - 1; i++) + { + if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii) + { + ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2]; + j = i; + } + } + heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1]; + for (i = j + 1; i < count - 1; i++) + { + heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2]; + heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1]; + } + //System.arraycopy(heightAndDepth, j + 1, heightAndDepth, j, count - j - 1); + count--; + } + + short lodCount = 0; + for (j = 0; j < count; j--) + { + height = heightAndDepth[j * 2]; + depth = heightAndDepth[j * 2 + 1]; + + if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2) + break; + + setVerticalData( + VerticalDataFormat.createVerticalData(height, depth, 0, false, false), + posX, + posZ, + lodCount); + lodCount++; + } + + //we update the count + setPositionData( + PositionDataFormat.setLodCount( + PositionDataFormat.createPositionData(0, correctLight, genMode), + lodCount), + posX, + posZ); //STEP 5// //we now get the top lods on each vertical index and we merge - //the color, the data + //the color, the data and ligth of all of them + boolean allDefault; + int tempColorData; + byte tempLightData; + int storedVerticalData; + int newVerticalData = 0; + int newColorData = 0; + byte newLightData = 0; + //As standard the vertical lods are ordered from top to bottom + for (int verticalIndex = lodCount; verticalIndex >= 0; verticalIndex--) + { + storedVerticalData = getVerticalData(posX, posZ, verticalIndex); + height = VerticalDataFormat.getHeight(storedVerticalData); + depth = VerticalDataFormat.getDepth(storedVerticalData); + + if ((depth == 0 && height == 0) || verticalIndex >= heightAndDepth.length / 2) + break; + + int numberOfChildren = 0; + int tempAlpha = 0; + int tempRed = 0; + int tempGreen = 0; + int tempBlue = 0; + int tempLightBlock = 0; + int tempLightSky = 0; + + for (int positionIndex = 0; positionIndex < size; positionIndex++) + { + tempPositionData = inputPositionData[positionIndex]; + + if (!PositionDataFormat.doesItExist(tempPositionData) || PositionDataFormat.isVoid(tempPositionData)) + continue; + for (int inputVerticalIndex = 0; inputVerticalIndex < inputVerticalSize; inputVerticalIndex++) + { + tempVerticalData = inputVerticalData[positionIndex * inputVerticalSize + inputVerticalIndex]; + tempColorData = inputColorData[positionIndex * inputVerticalSize + inputVerticalIndex]; + tempLightData = inputLightData[positionIndex * inputVerticalSize + inputVerticalIndex]; + if (VerticalDataFormat.doesItExist(tempVerticalData)) + { + if ((depth <= VerticalDataFormat.getDepth(tempVerticalData) && VerticalDataFormat.getDepth(tempVerticalData) <= height) + || (depth <= VerticalDataFormat.getHeight(tempVerticalData) && VerticalDataFormat.getHeight(tempVerticalData) <= height)) + { + if (VerticalDataFormat.getHeight(tempVerticalData) > VerticalDataFormat.getHeight(newVerticalData)) + { + newVerticalData = tempColorData; + newColorData = tempVerticalData; + newLightData = tempLightData; + } + } + } + else + break; + } + + numberOfChildren++; + tempAlpha += ColorFormat.getAlpha(newColorData); + tempRed += ColorFormat.getRed(newColorData); + tempGreen += ColorFormat.getGreen(newColorData); + tempBlue += ColorFormat.getBlue(newColorData); + tempLightBlock += LightFormat.getBlockLight(newLightData); + tempLightSky += LightFormat.getSkyLight(newLightData); + } + + //we have at least 1 child + tempAlpha = tempAlpha / numberOfChildren; + tempRed = tempRed / numberOfChildren; + tempGreen = tempGreen / numberOfChildren; + tempBlue = tempBlue / numberOfChildren; + tempLightBlock = tempLightBlock / numberOfChildren; + tempLightSky = tempLightSky / numberOfChildren; + setColorData(ColorFormat.createColorData(tempAlpha, tempRed, tempGreen, tempBlue), posX, posZ, verticalIndex); + setLightData(LightFormat.formatLightAsByte((byte) tempLightSky, (byte) tempLightBlock), posX, posZ, verticalIndex); + } - - /* + /* OLD CODE FOR REFERENCE int size = dataToMerge.length / inputVerticalData; // We initialize the arrays that are going to be used @@ -622,9 +900,9 @@ public class VerticalLevelContainer implements LevelContainer return dataPoint; */ - } - - @Override + } + + @Override public LevelContainer expand() { return new VerticalLevelContainer((byte) (getDetailLevel() - 1)); @@ -686,7 +964,7 @@ public class VerticalLevelContainer implements LevelContainer tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex)); index += 8; } - if(!DataPointUtil.doesItExist(dataContainer[i])) + if (!DataPointUtil.doesItExist(dataContainer[i])) allGenerated = false; } if (allGenerated)