From 2ec7cc8c6da0b139ff68d06f7b89c4291adc8bb2 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 5 Oct 2022 22:21:10 -0500 Subject: [PATCH] Refactor ColumnRenderSource --- .../lod/core/datatype/ILodRenderSource.java | 4 +- .../datatype/PlaceHolderRenderSource.java | 2 +- .../datatype/column/ColumnRenderSource.java | 404 +++++++++++------- .../column/accessor/ColumnFormat.java | 18 +- .../column/accessor/IColumnDatatype.java | 45 +- .../datatype/column/render/ColumnBox.java | 15 +- .../column/render/ColumnRenderBuffer.java | 12 +- .../transform/FullToColumnTransformer.java | 22 +- .../file/renderfile/RenderFileHandler.java | 3 +- 9 files changed, 310 insertions(+), 215 deletions(-) diff --git a/core/src/main/java/com/seibel/lod/core/datatype/ILodRenderSource.java b/core/src/main/java/com/seibel/lod/core/datatype/ILodRenderSource.java index 65f1abe2a..5310b64db 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/ILodRenderSource.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/ILodRenderSource.java @@ -43,6 +43,6 @@ public interface ILodRenderSource void fastWrite(ChunkSizedData chunkData, IDhClientLevel level); - /** Only override the data that has not been written directly using write(), and skip those that are empty */ - void weakWrite(ILodRenderSource source); + /** Overrides any data that has not been written directly using write(). Skips empty source dataPoints. */ + void updateFromRenderSource(ILodRenderSource source); } diff --git a/core/src/main/java/com/seibel/lod/core/datatype/PlaceHolderRenderSource.java b/core/src/main/java/com/seibel/lod/core/datatype/PlaceHolderRenderSource.java index 189d369d2..e0fff4c5f 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/PlaceHolderRenderSource.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/PlaceHolderRenderSource.java @@ -57,7 +57,7 @@ public class PlaceHolderRenderSource implements ILodRenderSource public void fastWrite(ChunkSizedData chunkData, IDhClientLevel level) { /* TODO */ } @Override - public void weakWrite(ILodRenderSource source) { /* TODO */ } + public void updateFromRenderSource(ILodRenderSource source) { /* TODO */ } } diff --git a/core/src/main/java/com/seibel/lod/core/datatype/column/ColumnRenderSource.java b/core/src/main/java/com/seibel/lod/core/datatype/column/ColumnRenderSource.java index 3e653b3a5..b8d855c09 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/column/ColumnRenderSource.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/column/ColumnRenderSource.java @@ -28,10 +28,14 @@ import java.nio.ByteOrder; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; +/** + * @author Leetom + * @version 2022-10-5 + */ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public static final boolean DO_SAFETY_CHECKS = true; + public static final boolean DO_SAFETY_CHECKS = true; // TODO: this could potentially be replaced with "ModInfo.IS_DEV_BUILD" public static final byte SECTION_SIZE_OFFSET = 6; public static final int SECTION_SIZE = 1 << SECTION_SIZE_OFFSET; public static final byte LATEST_VERSION = 1; @@ -98,29 +102,24 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype this.sectionPos = sectionPos; this.yOffset = level.getMinY(); this.verticalSize = inputData.readByte() & 0b01111111; - this.dataContainer = loadData(inputData, version, this.verticalSize); + this.dataContainer = this.loadData(inputData, version, this.verticalSize); this.airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * this.verticalSize]; this.debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE]; - debugFillFlag(0, 0, SECTION_SIZE, SECTION_SIZE, DebugSourceFlag.FILE); + this.fillDebugFlag(0, 0, SECTION_SIZE, SECTION_SIZE, DebugSourceFlag.FILE); } + //========================// + // datapoint manipulation // + //========================// - public void debugFillFlag(int ox, int oz, int w, int h, DebugSourceFlag flag) - { - for (int x = ox; x < ox + w; x++) - { - for (int z = oz; z < oz + h; z++) - { - debugSourceFlags[x * SECTION_SIZE + z] = flag; - } - } - } - - public DebugSourceFlag debugGetFlag(int ox, int oz) { return debugSourceFlags[ox * SECTION_SIZE + oz]; } - + /** + * Attempts to parse and load the given DataInputStream. + * + * @throws IOException if the version isn't supported + */ private long[] loadData(DataInputStream inputData, int version, int verticalSize) throws IOException { switch (version) @@ -128,65 +127,69 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype case 1: return readDataV1(inputData, verticalSize); default: - throw new IOException("Invalid Data: The version of the data is not supported"); + throw new IOException("Invalid Data: The data version [" + version + "] is not supported"); } } private long[] readDataV1(DataInputStream inputData, int tempMaxVerticalData) throws IOException { - int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData; + int maxNumberOfDataPoints = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData; + short tempMinHeight = Short.reverseBytes(inputData.readShort()); if (tempMinHeight == Short.MAX_VALUE) { //FIXME: Temp hack flag for marking a empty section - return new long[x]; + return new long[maxNumberOfDataPoints]; } - isEmpty = false; - byte[] data = new byte[x * Long.BYTES]; - ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); + + this.isEmpty = false; + byte[] data = new byte[maxNumberOfDataPoints * Long.BYTES]; + ByteBuffer byteBuffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); inputData.readFully(data); - long[] result = new long[x]; - bb.asLongBuffer().get(result); - if (tempMinHeight != yOffset) + + long[] result = new long[maxNumberOfDataPoints]; + byteBuffer.asLongBuffer().get(result); + if (tempMinHeight != this.yOffset) { for (int i = 0; i < result.length; i++) { - result[i] = ColumnFormat.shiftHeightAndDepth(result[i], (short) (tempMinHeight - yOffset)); + result[i] = ColumnFormat.shiftHeightAndDepth(result[i], (short) (tempMinHeight - this.yOffset)); } } return result; } @Override - public void clear(int posX, int posZ) + public void clearDataPoint(int posX, int posZ) { - for (int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++) - dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex] = - ColumnFormat.EMPTY_DATA; + for (int verticalIndex = 0; verticalIndex < this.verticalSize; verticalIndex++) + { + this.dataContainer[posX * SECTION_SIZE * this.verticalSize + posZ * this.verticalSize + verticalIndex] = ColumnFormat.EMPTY_DATA; + } } - @Override - public boolean addData(long data, int posX, int posZ, int verticalIndex) + public boolean setDataPoint(long data, int posX, int posZ, int verticalIndex) { - dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex] = data; + this.dataContainer[posX * SECTION_SIZE * this.verticalSize + posZ * this.verticalSize + verticalIndex] = data; return true; } @Override - public boolean copyVerticalData(IColumnDataView data, int posX, int posZ, boolean override) + public boolean copyVerticalData(IColumnDataView newData, int posX, int posZ, boolean overwriteDataWithSameGenerationMode) { if (DO_SAFETY_CHECKS) { - if (data.size() != verticalSize) - throw new IllegalArgumentException("data size not the same as vertical size"); + if (newData.size() != this.verticalSize) + throw new IllegalArgumentException("newData size not the same as this column's vertical size"); if (posX < 0 || posX >= SECTION_SIZE) throw new IllegalArgumentException("X position is out of bounds"); if (posZ < 0 || posZ >= SECTION_SIZE) throw new IllegalArgumentException("Z position is out of bounds"); } - int index = posX * SECTION_SIZE * verticalSize + posZ * verticalSize; - int compare = ColumnFormat.compareDatapointPriority(data.get(0), dataContainer[index]); - if (override) + + int dataOffset = posX * SECTION_SIZE * this.verticalSize + posZ * this.verticalSize; + int compare = ColumnFormat.compareDatapointPriority(newData.get(0), this.dataContainer[dataOffset]); + if (overwriteDataWithSameGenerationMode) { if (compare < 0) return false; @@ -196,146 +199,198 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype if (compare <= 0) return false; } - data.copyTo(dataContainer, index, data.size()); + + // copy the newData into this column's data + newData.copyTo(this.dataContainer, dataOffset, newData.size()); return true; } - @Override - public long getData(int posX, int posZ, int verticalIndex) { return dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex]; } + + + // TODO: @Override - public long[] getAllData(int posX, int posZ) + public long getDataPoint(int posX, int posZ, int verticalIndex) { return this.dataContainer[posX * SECTION_SIZE * this.verticalSize + posZ * this.verticalSize + verticalIndex]; } + + @Override + public long[] getVerticalDataPointArray(int posX, int posZ) { - long[] result = new long[verticalSize]; - int index = posX * SECTION_SIZE * verticalSize + posZ * verticalSize; - System.arraycopy(dataContainer, index, result, 0, verticalSize); + long[] result = new long[this.verticalSize]; + int index = posX * SECTION_SIZE * this.verticalSize + posZ * this.verticalSize; + System.arraycopy(this.dataContainer, index, result, 0, this.verticalSize); return result; } @Override - public ColumnArrayView getVerticalDataView(int posX, int posZ) + public ColumnArrayView getVerticalDataPointView(int posX, int posZ) { - return new ColumnArrayView(dataContainer, verticalSize, - posX * SECTION_SIZE * verticalSize + posZ * verticalSize, verticalSize); + return new ColumnArrayView(this.dataContainer, this.verticalSize, + posX * SECTION_SIZE * this.verticalSize + posZ * this.verticalSize, + this.verticalSize); } @Override - public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) { return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, quadX, quadZ, quadXSize, quadZSize); } + public ColumnQuadView getFullQuadView() { return getQuadViewOverRange(0, 0, SECTION_SIZE, SECTION_SIZE); } + @Override + public ColumnQuadView getQuadViewOverRange(int quadX, int quadZ, int quadXSize, int quadZSize) { return new ColumnQuadView(this.dataContainer, SECTION_SIZE, this.verticalSize, quadX, quadZ, quadXSize, quadZSize); } @Override - public ColumnQuadView getFullQuad() { return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, 0, 0, SECTION_SIZE, SECTION_SIZE); } + public int getVerticalSize() { return this.verticalSize; } + + + + //========================// + // data update and output // + //========================// + + /** @return true if this object had data written in every column */ + boolean writeData(DataOutputStream outputStream) throws IOException + { + outputStream.writeByte(getDataDetail()); + outputStream.writeByte((byte) this.verticalSize); + + if (isEmpty) + { + outputStream.writeByte(Short.MAX_VALUE & 0xFF); + outputStream.writeByte((Short.MAX_VALUE >> 8) & 0xFF); + + return false; + } + else + { + // FIXME: yOffset is a int, but we only are writing a short. + outputStream.writeByte((byte) (this.yOffset & 0xFF)); + outputStream.writeByte((byte) ((this.yOffset >> 8) & 0xFF)); + + // write the data for each column + boolean allGenerated = true; + for (int x = 0; x < SECTION_SIZE * SECTION_SIZE; x++) + { + for (int z = 0; z < verticalSize; z++) + { + long currentDatapoint = dataContainer[x * verticalSize + z]; + if (ColumnFormat.doesDataPointExist(currentDatapoint)) + { + // TODO: the "1" is a placeholder debug line + currentDatapoint = ColumnFormat.overrideGenerationMode(currentDatapoint, (byte) 1); + } + outputStream.writeLong(Long.reverseBytes(currentDatapoint)); + } + + if (!ColumnFormat.doesDataPointExist(dataContainer[x])) + { + allGenerated = false; + } + } + + return allGenerated; + } + } @Override - public int getVerticalSize() { return verticalSize; } + public void updateFromRenderSource(ILodRenderSource source) + { + // TODO if we can only write this one type of data isn't it dangerous to have it in the interface? + LodUtil.assertTrue(source instanceof ColumnRenderSource); + ColumnRenderSource src = (ColumnRenderSource) source; + + // validate we are writing for the same location + LodUtil.assertTrue(src.sectionPos.equals(this.sectionPos)); + // validate both objects have the same number of dataPoints + LodUtil.assertTrue(src.verticalSize == this.verticalSize); + + + if (src.isEmpty) + // the source is empty, don't attempt to update anything + return; + // the source isn't empty, this object won't be empty after the method finishes + this.isEmpty = false; + + + for (int i = 0; i < this.dataContainer.length; i += this.verticalSize) + { + int thisGenMode = ColumnFormat.getGenerationMode(this.dataContainer[i]); + int srcGenMode = ColumnFormat.getGenerationMode(src.dataContainer[i]); + + if (srcGenMode == 0) + // the source hasn't been generated, don't write it + continue; + + // this object's column is older than the source's column, update it + if (thisGenMode <= srcGenMode) + { + ColumnArrayView thisColumnArrayView = new ColumnArrayView(this.dataContainer, this.verticalSize, i, this.verticalSize); + ColumnArrayView srcColumnArrayView = new ColumnArrayView(src.dataContainer, src.verticalSize, i, src.verticalSize); + thisColumnArrayView.copyFrom(srcColumnArrayView); + + this.debugSourceFlags[i / this.verticalSize] = src.debugSourceFlags[i / this.verticalSize]; + } + } + } @Override - public boolean doesItExist(int posX, int posZ) { return ColumnFormat.doesItExist(getSingleData(posX, posZ)); } + public void fastWrite(ChunkSizedData chunkData, IDhClientLevel level) { FullToColumnTransformer.writeFullDataChunkToColumnData(this, level, chunkData); } + + + + //=====================// + // data helper methods // + //=====================// + + @Override + public boolean doesDataPointExist(int posX, int posZ) { return ColumnFormat.doesDataPointExist(this.getFirstDataPoint(posX, posZ)); } @Override public void generateData(IColumnDatatype lowerDataContainer, int posX, int posZ) { - ColumnQuadView quadView = lowerDataContainer.getDataInQuad(posX * 2, posZ * 2, 2, 2); - ColumnArrayView outputView = getVerticalDataView(posX, posZ); + ColumnArrayView outputView = this.getVerticalDataPointView(posX, posZ); + ColumnQuadView quadView = lowerDataContainer.getQuadViewOverRange(posX * 2, posZ * 2, 2, 2); outputView.mergeMultiDataFrom(quadView); } - boolean writeData(DataOutputStream output) throws IOException - { - output.writeByte(getDataDetail()); - output.writeByte((byte) verticalSize); - // FIXME: yOffset is a int, but we only are writing a short. - if (isEmpty) - { - output.writeByte(Short.MAX_VALUE & 0xFF); - output.writeByte((Short.MAX_VALUE >> 8) & 0xFF); - return false; - } - output.writeByte((byte) (yOffset & 0xFF)); - output.writeByte((byte) ((yOffset >> 8) & 0xFF)); - boolean allGenerated = true; - int x = SECTION_SIZE * SECTION_SIZE; - for (int i = 0; i < x; i++) - { - for (int j = 0; j < verticalSize; j++) - { - long current = dataContainer[i * verticalSize + j]; - if (ColumnFormat.doesItExist(current)) - current = ColumnFormat.overrideGenerationMode(current, (byte) 1); - output.writeLong(Long.reverseBytes(current)); - } - if (!ColumnFormat.doesItExist(dataContainer[i])) - allGenerated = false; - } - return allGenerated; - } - - public String toString() - { - String LINE_DELIMITER = "\n"; - String DATA_DELIMITER = " "; - String SUBDATA_DELIMITER = ","; - StringBuilder stringBuilder = new StringBuilder(); - int size = sectionPos.getWidth().value; - stringBuilder.append(sectionPos); - stringBuilder.append(LINE_DELIMITER); - for (int z = 0; z < size; z++) - { - for (int x = 0; x < size; x++) - { - for (int y = 0; y < verticalSize; y++) - { - //Converting the dataToHex - stringBuilder.append(Long.toHexString(getData(x, z, y))); - if (y != verticalSize - 1) - stringBuilder.append(SUBDATA_DELIMITER); - } - if (x != size - 1) - stringBuilder.append(DATA_DELIMITER); - } - if (z != size - 1) - stringBuilder.append(LINE_DELIMITER); - } - return stringBuilder.toString(); - } + @Override + public int getMaxLodCount() { return SECTION_SIZE * SECTION_SIZE * getVerticalSize(); } @Override - public int getMaxNumberOfLods() { return SECTION_SIZE * SECTION_SIZE * getVerticalSize(); } + public long getRoughRamUsageInBytes() { return (long) this.dataContainer.length * Long.BYTES; } - @Override - public long getRoughRamUsageInBytes() { return (long) dataContainer.length * Long.BYTES; } + public DhSectionPos getSectionPos() { return this.sectionPos; } - public DhSectionPos getSectionPos() { return sectionPos; } - - public byte getDataDetail() { return (byte) (sectionPos.sectionDetail - SECTION_SIZE_OFFSET); } + public byte getDataDetail() { return (byte) (this.sectionPos.sectionDetail - SECTION_SIZE_OFFSET); } @Override public byte getDetailOffset() { return SECTION_SIZE_OFFSET; } + + //================// + // Render Methods // + //================// + private void tryBuildBuffer(IDhClientLevel level, LodQuadTree quadTree) { - if (inBuildRenderBuffer == null && !ColumnRenderBuffer.isBusy() && !isEmpty) + if (this.inBuildRenderBuffer == null && !ColumnRenderBuffer.isBusy() && !this.isEmpty) { ColumnRenderSource[] data = new ColumnRenderSource[ELodDirection.ADJ_DIRECTIONS.length]; for (ELodDirection direction : ELodDirection.ADJ_DIRECTIONS) { - LodRenderSection section = quadTree.getSection(sectionPos.getAdjacent(direction)); //FIXME: Handle traveling through different detail levels + LodRenderSection section = quadTree.getSection(this.sectionPos.getAdjacent(direction)); //FIXME: Handle traveling through different detail levels if (section != null && section.getRenderSource() != null && section.getRenderSource() instanceof ColumnRenderSource) { data[direction.ordinal() - 2] = ((ColumnRenderSource) section.getRenderSource()); } } - inBuildRenderBuffer = ColumnRenderBuffer.build(level, usedBuffer, this, data); + this.inBuildRenderBuffer = ColumnRenderBuffer.build(level, this.usedBuffer, this, data); } } private void cancelBuildBuffer() { - if (inBuildRenderBuffer != null) + if (this.inBuildRenderBuffer != null) { //LOGGER.info("Cancelling build of render buffer for {}", sectionPos); - inBuildRenderBuffer.cancel(true); - inBuildRenderBuffer = null; + this.inBuildRenderBuffer.cancel(true); + this.inBuildRenderBuffer = null; } } @@ -355,37 +410,40 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype @Override public boolean trySwapRenderBuffer(LodQuadTree quadTree, AtomicReference referenceSlot) { - if (lastNs != -1 && System.nanoTime() - lastNs < SWAP_TIMEOUT) + if (this.lastNs != -1 && System.nanoTime() - this.lastNs < SWAP_TIMEOUT) { return false; } - if (inBuildRenderBuffer != null) + + if (this.inBuildRenderBuffer != null) { - if (inBuildRenderBuffer.isDone()) + if (this.inBuildRenderBuffer.isDone()) { - lastNs = System.nanoTime(); + this.lastNs = System.nanoTime(); //LOGGER.info("Swapping render buffer for {}", sectionPos); - RenderBuffer newBuffer = inBuildRenderBuffer.join(); + RenderBuffer newBuffer = this.inBuildRenderBuffer.join(); RenderBuffer oldBuffer = referenceSlot.getAndSet(newBuffer); if (oldBuffer instanceof ColumnRenderBuffer) { - ColumnRenderBuffer swapped = usedBuffer.swap((ColumnRenderBuffer) oldBuffer); + ColumnRenderBuffer swapped = this.usedBuffer.swap((ColumnRenderBuffer) oldBuffer); LodUtil.assertTrue(swapped == null); } - inBuildRenderBuffer = null; + this.inBuildRenderBuffer = null; return true; } } else { - if (!isEmpty) + if (!this.isEmpty) { if (ColumnRenderBuffer.isBusy()) { - lastNs += (long) (SWAP_BUSY_COLLISION_TIMEOUT * Math.random()); + this.lastNs += (long) (SWAP_BUSY_COLLISION_TIMEOUT * Math.random()); } else - tryBuildBuffer(level, quadTree); + { + this.tryBuildBuffer(this.level, quadTree); + } } } return false; @@ -405,40 +463,68 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype public boolean isValid() { return true; } @Override - public boolean isEmpty() { return isEmpty; } + public boolean isEmpty() { return this.isEmpty; } + public void markNotEmpty() { this.isEmpty = false; } - public void markNotEmpty() { isEmpty = false; } - @Override - public void weakWrite(ILodRenderSource source) + + //=======// + // debug // + //=======// + + /** Sets the debug flag for the given area */ + public void fillDebugFlag(int startX, int startZ, int width, int height, DebugSourceFlag flag) { - LodUtil.assertTrue(source instanceof ColumnRenderSource); - ColumnRenderSource src = (ColumnRenderSource) source; - - LodUtil.assertTrue(src.sectionPos.equals(sectionPos)); - LodUtil.assertTrue(src.verticalSize == verticalSize); - - if (src.isEmpty) - return; - isEmpty = false; - - for (int i = 0; i < dataContainer.length; i += verticalSize) + for (int x = startX; x < startX + width; x++) { - int genMode = ColumnFormat.getGenerationMode(dataContainer[i]); - int srcGenMode = ColumnFormat.getGenerationMode(src.dataContainer[i]); - if (srcGenMode == 0) - continue; - if (genMode <= srcGenMode) + for (int z = startZ; z < startZ + height; z++) { - new ColumnArrayView(dataContainer, verticalSize, i, verticalSize).copyFrom( - new ColumnArrayView(src.dataContainer, verticalSize, i, verticalSize)); - debugSourceFlags[i / verticalSize] = src.debugSourceFlags[i / verticalSize]; + debugSourceFlags[x * SECTION_SIZE + z] = flag; } } } + public DebugSourceFlag debugGetFlag(int ox, int oz) { return debugSourceFlags[ox * SECTION_SIZE + oz]; } + + + + //==============// + // base methods // + //==============// + @Override - public void fastWrite(ChunkSizedData chunkData, IDhClientLevel level) { FullToColumnTransformer.writeFullDataChunkToColumnData(this, level, chunkData); } + public String toString() + { + String LINE_DELIMITER = "\n"; + String DATA_DELIMITER = " "; + String SUBDATA_DELIMITER = ","; + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(sectionPos); + stringBuilder.append(LINE_DELIMITER); + + int size = sectionPos.getWidth().value; + for (int z = 0; z < size; z++) + { + for (int x = 0; x < size; x++) + { + for (int y = 0; y < verticalSize; y++) + { + //Converting the dataToHex + stringBuilder.append(Long.toHexString(getDataPoint(x, z, y))); + if (y != verticalSize - 1) + stringBuilder.append(SUBDATA_DELIMITER); + } + + if (x != size - 1) + stringBuilder.append(DATA_DELIMITER); + } + + if (z != size - 1) + stringBuilder.append(LINE_DELIMITER); + } + return stringBuilder.toString(); + } diff --git a/core/src/main/java/com/seibel/lod/core/datatype/column/accessor/ColumnFormat.java b/core/src/main/java/com/seibel/lod/core/datatype/column/accessor/ColumnFormat.java index c7b9fbea0..34714d477 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/column/accessor/ColumnFormat.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/column/accessor/ColumnFormat.java @@ -208,7 +208,7 @@ public class ColumnFormat { public static byte getGenerationMode(long dataPoint) { byte genMode = (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK); - if (warnLogger.canMaybeLog() && doesItExist(dataPoint) && genMode == 0) { + if (warnLogger.canMaybeLog() && doesDataPointExist(dataPoint) && genMode == 0) { warnLogger.warnInc("Existing datapoint with genMode 0 detected! This is invalid in DataPoint version 10!" + " This may be caused by old data that has not been updated correctly."); return 1; @@ -224,7 +224,7 @@ public class ColumnFormat { return (((dataPoint >>> DEPTH_SHIFT) & HEIGHT_DEPTH_MASK) == 0); } - public static boolean doesItExist(long dataPoint) { + public static boolean doesDataPointExist(long dataPoint) { return dataPoint != 0; } @@ -238,7 +238,7 @@ public class ColumnFormat { */ @SuppressWarnings("unused") public static String toString(long dataPoint) { - if (!doesItExist(dataPoint)) return "null"; + if (!doesDataPointExist(dataPoint)) return "null"; if (isVoid(dataPoint)) return "void"; return "H:" + getHeight(dataPoint) + " D:" + getDepth(dataPoint) + @@ -347,7 +347,7 @@ public class ColumnFormat { for (int index = 0; index < dataCount; index++) { tempData = sourceData.get(index * inputVerticalSize); allVoid = allVoid && ColumnFormat.isVoid(tempData); - allEmpty = allEmpty && !ColumnFormat.doesItExist(tempData); + allEmpty = allEmpty && !ColumnFormat.doesDataPointExist(tempData); } //We check if there is any data that's not empty or void @@ -375,7 +375,7 @@ public class ColumnFormat { for (int index = 0; index < dataCount; index++) { if (indeces[index] < inputVerticalSize) { tempData = sourceData.get(index * inputVerticalSize + indeces[index]); - if (!ColumnFormat.isVoid(tempData) && ColumnFormat.doesItExist(tempData)) { + if (!ColumnFormat.isVoid(tempData) && ColumnFormat.doesDataPointExist(tempData)) { tempHeight = ColumnFormat.getHeight(tempData); tempDepth = ColumnFormat.getDepth(tempData); if (tempDepth >= newHeight) { @@ -452,7 +452,7 @@ public class ColumnFormat { for (int index = 0; index < dataCount; index++) { if (indeces[index] < inputVerticalSize) { tempData = sourceData.get(index * inputVerticalSize + indeces[index]); - stillHasDataToCheck |= !ColumnFormat.isVoid(tempData) && ColumnFormat.doesItExist(tempData); + stillHasDataToCheck |= !ColumnFormat.isVoid(tempData) && ColumnFormat.doesDataPointExist(tempData); } } } @@ -522,7 +522,7 @@ public class ColumnFormat { //we scan the lods in the position from top to bottom while (dataIndexesCache[index] < inputVerticalSize) { singleData = sourceData.get(index * inputVerticalSize + dataIndexesCache[index]); - if (doesItExist(singleData) && !isVoid(singleData)) { + if (doesDataPointExist(singleData) && !isVoid(singleData)) { dataIndexesCache[index]++; if ((depth <= getDepth(singleData) && getDepth(singleData) < height) || (depth < getHeight(singleData) && getHeight(singleData) <= height)) { @@ -532,11 +532,11 @@ public class ColumnFormat { } else break; } - if (!doesItExist(data)) { + if (!doesDataPointExist(data)) { data = createVoidDataPoint(genMode); } - if (doesItExist(data)) { + if (doesDataPointExist(data)) { allEmpty = false; if (!isVoid(data)) { numberOfChildren++; diff --git a/core/src/main/java/com/seibel/lod/core/datatype/column/accessor/IColumnDatatype.java b/core/src/main/java/com/seibel/lod/core/datatype/column/accessor/IColumnDatatype.java index 935efbef8..7ba6036e6 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/column/accessor/IColumnDatatype.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/column/accessor/IColumnDatatype.java @@ -9,37 +9,48 @@ public interface IColumnDatatype return 1 << getDetailOffset(); } - int getMaxNumberOfLods(); + /** Returns how many LODs could be contained by this object. */ + int getMaxLodCount(); long getRoughRamUsageInBytes(); int getVerticalSize(); - boolean doesItExist(int posX, int posZ); + boolean doesDataPointExist(int posX, int posZ); - long getData(int posX, int posZ, int verticalIndex); + /** Returns the datapoint for the given relative coordinates and vertical index */ + long getDataPoint(int posX, int posZ, int verticalIndex); + /** + * Returns the top datapoint for the given relative coordinates
+ * Returns the empty datapoint if no data is present. + */ + default long getFirstDataPoint(int posX, int posZ) { return getDataPoint(posX, posZ, 0); } - default long getSingleData(int posX, int posZ) { return getData(posX, posZ, 0); } + /** Returns every datapoint in the vertical slice at the given position as an array */ + long[] getVerticalDataPointArray(int posX, int posZ); + /** Returns every datapoint in the vertical slice at the given position as a ColumnArrayView */ + ColumnArrayView getVerticalDataPointView(int posX, int posZ); - long[] getAllData(int posX, int posZ); + /** Returns a QuadView that covers this whole object */ + ColumnQuadView getFullQuadView(); + /** Returns a QuadView over the give coordinate range */ + ColumnQuadView getQuadViewOverRange(int quadX, int quadZ, int quadXSize, int quadZSize); - ColumnArrayView getVerticalDataView(int posX, int posZ); + /** clears the datapoint stored at the relative position */ + void clearDataPoint(int posX, int posZ); - ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize); - - ColumnQuadView getFullQuad(); - - /** clears all data at the relative position */ - void clear(int posX, int posZ); - - /** adds the given data to the relative position and vertical index */ - boolean addData(long data, int posX, int posZ, int verticalIndex); + /** + * adds/sets the given datapoint at the relative position and vertical index + * @return true if the datapoint was added/set + */ + boolean setDataPoint(long data, int posX, int posZ, int verticalIndex); /** * This methods will add the data in the given position if certain condition are satisfied - * @param override if override is true we can override data created with same generation mode + * @param overwriteDataWithSameGenerationMode if false old data will only be overwritten if it was generated with a lower priority than the newData + * @return true if the newData was successfully added, false otherwise */ - boolean copyVerticalData(IColumnDataView data, int posX, int posZ, boolean override); + boolean copyVerticalData(IColumnDataView newData, int posX, int posZ, boolean overwriteDataWithSameGenerationMode); void generateData(IColumnDatatype lowerDataContainer, int posX, int posZ); diff --git a/core/src/main/java/com/seibel/lod/core/datatype/column/render/ColumnBox.java b/core/src/main/java/com/seibel/lod/core/datatype/column/render/ColumnBox.java index d8e58b4f7..02bc61577 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/column/render/ColumnBox.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/column/render/ColumnBox.java @@ -25,7 +25,6 @@ import com.seibel.lod.core.render.renderer.LodRenderer; import com.seibel.lod.core.enums.ELodDirection; import com.seibel.lod.core.dependencyInjection.SingletonInjector; import com.seibel.lod.core.util.ColorUtil; -import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.util.MathUtil; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -40,7 +39,7 @@ public class ColumnBox short maxY = (short) (y + ySize); short maxZ = (short) (z + zSize); byte skyLightTop = skyLight; - byte skyLightBot = ColumnFormat.doesItExist(botData) ? ColumnFormat.getLightSky(botData) : 0; + byte skyLightBot = ColumnFormat.doesDataPointExist(botData) ? ColumnFormat.getLightSky(botData) : 0; boolean isTransparent = ColorUtil.getAlpha(color)<255 && LodRenderer.transparencyEnabled; boolean isTopTransparent = ColumnFormat.getAlpha(topData)<255 && LodRenderer.transparencyEnabled; @@ -51,15 +50,15 @@ public class ColumnBox //We skip if // current block is not transparent: we check if the adj block is attached and opaque - boolean skipTop = ColumnFormat.doesItExist(topData) && (ColumnFormat.getDepth(topData) == maxY) && !isTopTransparent; - boolean skipBot = ColumnFormat.doesItExist(botData) && (ColumnFormat.getHeight(botData) == y) && !isBotTransparent; + boolean skipTop = ColumnFormat.doesDataPointExist(topData) && (ColumnFormat.getDepth(topData) == maxY) && !isTopTransparent; + boolean skipBot = ColumnFormat.doesDataPointExist(botData) && (ColumnFormat.getHeight(botData) == y) && !isBotTransparent; if(LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor) { - if (!isTransparent && isTopTransparent && ColumnFormat.doesItExist(topData)) { + if (!isTransparent && isTopTransparent && ColumnFormat.doesDataPointExist(topData)) { skyLightTop = (byte) MathUtil.clamp(0, 15 - (ColumnFormat.getHeight(topData) - y), 15); ySize = (short) (ColumnFormat.getHeight(topData) - y - 1); //y = (short) (DataPointUtil.getHeight(topData) - 2); //ySize = 1; - } else if (isTransparent && !isBotTransparent && ColumnFormat.doesItExist(botData)) { + } else if (isTransparent && !isBotTransparent && ColumnFormat.doesDataPointExist(botData)) { y = (short) (y + ySize - 1); ySize = 1; } @@ -183,7 +182,7 @@ public class ColumnBox byte nextSkyLight = upSkyLight; boolean isTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled; boolean lastWasTransparent = false; - for (i = 0; i < dataPoint.size() && ColumnFormat.doesItExist(adjData.get(i)) + for (i = 0; i < dataPoint.size() && ColumnFormat.doesDataPointExist(adjData.get(i)) && !ColumnFormat.isVoid(adjData.get(i)); i++) { long adjPoint = adjData.get(i); @@ -328,7 +327,7 @@ public class ColumnBox previousDepth = depth; firstFace = false; nextSkyLight = upSkyLight; - if (i + 1 < adjData.size() && ColumnFormat.doesItExist(adjData.get(i + 1))) + if (i + 1 < adjData.size() && ColumnFormat.doesDataPointExist(adjData.get(i + 1))) nextSkyLight = ColumnFormat.getLightSky(adjData.get(i + 1)); lastWasTransparent = isAdjTransparent; } diff --git a/core/src/main/java/com/seibel/lod/core/datatype/column/render/ColumnRenderBuffer.java b/core/src/main/java/com/seibel/lod/core/datatype/column/render/ColumnRenderBuffer.java index e2601b601..9c508823e 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/column/render/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/column/render/ColumnRenderBuffer.java @@ -298,8 +298,8 @@ public class ColumnRenderBuffer extends RenderBuffer { for (int z = 0; z < ColumnRenderSource.SECTION_SIZE; z++) { UncheckedInterruptedException.throwIfInterrupted(); - ColumnArrayView posData = region.getVerticalDataView(x, z); - if (posData.size() == 0 || !ColumnFormat.doesItExist(posData.get(0)) + ColumnArrayView posData = region.getVerticalDataPointView(x, z); + if (posData.size() == 0 || !ColumnFormat.doesDataPointExist(posData.get(0)) || ColumnFormat.isVoid(posData.get(0))) continue; ColumnRenderSource.DebugSourceFlag debugSourceFlag = region.debugGetFlag(x, z); @@ -355,11 +355,11 @@ public class ColumnRenderBuffer extends RenderBuffer { if (adjDetail == detailLevel || adjDetail > detailLevel) { adjData[lodDirection.ordinal() - 2] = new ColumnArrayView[1]; - adjData[lodDirection.ordinal() - 2][0] = adjRegion.getVerticalDataView(xAdj, zAdj); + adjData[lodDirection.ordinal() - 2][0] = adjRegion.getVerticalDataPointView(xAdj, zAdj); } else { adjData[lodDirection.ordinal() - 2] = new ColumnArrayView[2]; - adjData[lodDirection.ordinal() - 2][0] = adjRegion.getVerticalDataView(xAdj, zAdj); - adjData[lodDirection.ordinal() - 2][1] = adjRegion.getVerticalDataView( + adjData[lodDirection.ordinal() - 2][0] = adjRegion.getVerticalDataPointView(xAdj, zAdj); + adjData[lodDirection.ordinal() - 2][1] = adjRegion.getVerticalDataPointView( xAdj + (lodDirection.getAxis()== ELodDirection.Axis.X ? 0 : 1), zAdj + (lodDirection.getAxis()== ELodDirection.Axis.Z ? 0 : 1)); } @@ -375,7 +375,7 @@ public class ColumnRenderBuffer extends RenderBuffer { long data = posData.get(i); // If the data is not renderable (Void or non-existing) we stop since there is // no data left in this position - if (ColumnFormat.isVoid(data) || !ColumnFormat.doesItExist(data)) + if (ColumnFormat.isVoid(data) || !ColumnFormat.doesDataPointExist(data)) break; long adjDataTop = i - 1 >= 0 ? posData.get(i - 1) : ColumnFormat.EMPTY_DATA; diff --git a/core/src/main/java/com/seibel/lod/core/datatype/transform/FullToColumnTransformer.java b/core/src/main/java/com/seibel/lod/core/datatype/transform/FullToColumnTransformer.java index 0456ff00a..e5014ff1c 100644 --- a/core/src/main/java/com/seibel/lod/core/datatype/transform/FullToColumnTransformer.java +++ b/core/src/main/java/com/seibel/lod/core/datatype/transform/FullToColumnTransformer.java @@ -40,13 +40,13 @@ public class FullToColumnTransformer { int baseZ = pos.getCorner().getCorner().z; for (int x = 0; x < pos.getWidth(dataDetail).value; x++) { for (int z = 0; z < pos.getWidth(dataDetail).value; z++) { - ColumnArrayView columnArrayView = columnSource.getVerticalDataView(x, z); + ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); SingleFullArrayView fullArrayView = data.get(x, z); convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView, 1); - if (fullArrayView.doesItExist()) LodUtil.assertTrue(columnSource.doesItExist(x, z)); + if (fullArrayView.doesItExist()) LodUtil.assertTrue(columnSource.doesDataPointExist(x, z)); } } - columnSource.debugFillFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); + columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); // } else if (dataDetail == 0 && columnSource.getDataDetail() > dataDetail) { // byte deltaDetail = (byte) (columnSource.getDataDetail() - dataDetail); // int perColumnWidth = 1 << deltaDetail; @@ -82,10 +82,10 @@ public class FullToColumnTransformer { for (int z = 0; z < pos.getWidth(dataDetail).value; z++) { SingleFullArrayView fullArrayView = data.tryGet(x, z); if (fullArrayView == null) continue; - ColumnArrayView columnArrayView = columnSource.getVerticalDataView(x, z); + ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView, 1); - columnSource.debugFillFlag(x, z, 1, 1, ColumnRenderSource.DebugSourceFlag.SPARSE); - if (fullArrayView.doesItExist()) LodUtil.assertTrue(columnSource.doesItExist(x, z)); + columnSource.fillDebugFlag(x, z, 1, 1, ColumnRenderSource.DebugSourceFlag.SPARSE); + if (fullArrayView.doesItExist()) LodUtil.assertTrue(columnSource.doesDataPointExist(x, z)); } } } else { @@ -112,15 +112,15 @@ public class FullToColumnTransformer { throw new IllegalArgumentException("Data offset is out of bounds"); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { - ColumnArrayView columnArrayView = render.getVerticalDataView(renderOffsetX + x, renderOffsetZ + z); + ColumnArrayView columnArrayView = render.getVerticalDataPointView(renderOffsetX + x, renderOffsetZ + z); SingleFullArrayView fullArrayView = data.get(x, z); convertColumnData(level, blockX + perRenderWidth * (renderOffsetX+x), blockZ + perRenderWidth * (renderOffsetZ+z), columnArrayView, fullArrayView, 2); - if (fullArrayView.doesItExist()) LodUtil.assertTrue(render.doesItExist(renderOffsetX + x, renderOffsetZ + z)); + if (fullArrayView.doesItExist()) LodUtil.assertTrue(render.doesDataPointExist(renderOffsetX + x, renderOffsetZ + z)); } } - render.debugFillFlag(renderOffsetX, renderOffsetZ, 16, 16, ColumnRenderSource.DebugSourceFlag.DIRECT); + render.fillDebugFlag(renderOffsetX, renderOffsetZ, 16, 16, ColumnRenderSource.DebugSourceFlag.DIRECT); } else { final int dataPerRender = 1 << (render.getDataDetail() - data.dataDetail); final int dataSize = 16 / dataPerRender; @@ -141,11 +141,11 @@ public class FullToColumnTransformer { columnArrayView, fullArrayView, 2); } } - ColumnArrayView downSampledArrayView = render.getVerticalDataView(renderOffsetX + x, renderOffsetZ + z); + ColumnArrayView downSampledArrayView = render.getVerticalDataPointView(renderOffsetX + x, renderOffsetZ + z); downSampledArrayView.mergeMultiDataFrom(tempQuadView); } } - render.debugFillFlag(renderOffsetX, renderOffsetZ, dataSize, dataSize, ColumnRenderSource.DebugSourceFlag.DIRECT); + render.fillDebugFlag(renderOffsetX, renderOffsetZ, dataSize, dataSize, ColumnRenderSource.DebugSourceFlag.DIRECT); } } diff --git a/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderFileHandler.java b/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderFileHandler.java index 9ee39089b..cc97afebf 100644 --- a/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderFileHandler.java +++ b/core/src/main/java/com/seibel/lod/core/file/renderfile/RenderFileHandler.java @@ -17,7 +17,6 @@ import com.seibel.lod.core.config.Config; import com.seibel.lod.core.logging.DhLoggerBuilder; import com.seibel.lod.core.util.LodUtil; import org.apache.logging.log4j.Logger; -import org.lwjgl.system.CallbackI; import java.io.File; import java.io.IOException; @@ -241,7 +240,7 @@ public class RenderFileHandler implements IRenderSourceProvider { ILodRenderSource newData, long newDataVersion) { if (target == null) return; if (newData == null) return; - target.weakWrite(newData); + target.updateFromRenderSource(newData); file.metaData.dataVersion.set(newDataVersion); file.metaData.dataLevel = target.getDataDetail(); file.loader = AbstractRenderSourceLoader.getLoader(target.getClass(), target.getRenderVersion());