Basic refactoring

This commit is contained in:
James Seibel
2022-10-02 22:22:54 -05:00
parent b8af1794a6
commit ce991cd912
3 changed files with 491 additions and 435 deletions
@@ -28,394 +28,434 @@ import java.nio.ByteOrder;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype {
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final boolean DO_SAFETY_CHECKS = true;
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;
public static final long TYPE_ID = "ColumnRenderSource".hashCode();
public static final int AIR_LODS_SIZE = 16;
public static final int AIR_SECTION_SIZE = SECTION_SIZE/AIR_LODS_SIZE;
public final int verticalSize;
public final DhSectionPos sectionPos;
public final int yOffset;
public final long[] dataContainer;
public final int[] airDataContainer;
public enum DebugSourceFlag {
FULL(ColorUtil.BLUE),
DIRECT(ColorUtil.WHITE),
SPARSE(ColorUtil.YELLOW),
FILE(ColorUtil.BROWN);
public final int color;
DebugSourceFlag(int color) {
this.color = color;
}
}
public final DebugSourceFlag[] debugSourceFlags;
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];
}
private boolean isEmpty = true;
/**
* Constructor of the ColumnDataType
* @param maxVerticalSize the maximum vertical size of the container
*/
public ColumnRenderSource(DhSectionPos sectionPos, int maxVerticalSize, int yOffset) {
verticalSize = maxVerticalSize;
dataContainer = new long[SECTION_SIZE * SECTION_SIZE * verticalSize];
airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE];
this.sectionPos = sectionPos;
this.yOffset = yOffset;
}
private long[] loadData(DataInputStream inputData, int version, int verticalSize) throws IOException {
switch (version) {
case 1:
return readDataV1(inputData, verticalSize);
default:
throw new IOException("Invalid Data: The version of the data is not supported");
}
}
private long[] readDataV1(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = 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];
}
isEmpty = false;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
if (tempMinHeight != yOffset) {
for (int i=0; i<result.length; i++) {
result[i] = ColumnFormat.shiftHeightAndDepth(result[i], (short) (tempMinHeight - yOffset));
}
}
return result;
}
// Load from data stream with maxVerticalSize loaded from the data stream
public ColumnRenderSource(DhSectionPos sectionPos, DataInputStream inputData, int version, IDhLevel level) throws IOException {
this.sectionPos = sectionPos;
yOffset = level.getMinY();
byte detailLevel = inputData.readByte();
if (sectionPos.sectionDetail - SECTION_SIZE_OFFSET != detailLevel) {
throw new IOException("Invalid data: detail level does not match");
}
verticalSize = inputData.readByte() & 0b01111111;
dataContainer = loadData(inputData, version, verticalSize);
airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE];
debugFillFlag(0, 0, SECTION_SIZE, SECTION_SIZE, DebugSourceFlag.FILE);
}
@Override
public void clear(int posX, int posZ)
{
for (int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++)
dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex] =
ColumnFormat.EMPTY_DATA;
}
@Override
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex] = data;
return true;
}
@Override
public boolean copyVerticalData(IColumnDataView data, int posX, int posZ, boolean override) {
if (DO_SAFETY_CHECKS) {
if (data.size() != verticalSize)
throw new IllegalArgumentException("data size not the same as 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) {
if (compare<0) return false;
} else {
if (compare<=0) return false;
}
data.copyTo(dataContainer, index, data.size());
return true;
}
@Override
public long getData(int posX, int posZ, int verticalIndex)
{
return dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex];
}
@Override
public long[] getAllData(int posX, int posZ)
{
long[] result = new long[verticalSize];
int index = posX * SECTION_SIZE * verticalSize + posZ * verticalSize;
System.arraycopy(dataContainer, index, result, 0, verticalSize);
return result;
}
@Override
public ColumnArrayView getVerticalDataView(int posX, int posZ) {
return new ColumnArrayView(dataContainer, verticalSize,
posX * SECTION_SIZE * verticalSize + posZ * verticalSize, verticalSize);
}
@Override
public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) {
return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, quadX, quadZ, quadXSize, quadZSize);
}
@Override
public ColumnQuadView getFullQuad() {
return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, 0, 0, SECTION_SIZE, SECTION_SIZE);
}
@Override
public int getVerticalSize()
{
return verticalSize;
}
@Override
public boolean doesItExist(int posX, int posZ)
{
return ColumnFormat.doesItExist(getSingleData(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);
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 getMaxNumberOfLods()
{
return SECTION_SIZE * SECTION_SIZE * getVerticalSize();
}
@Override
public long getRoughRamUsage()
{
return (long) dataContainer.length * Long.BYTES;
}
public DhSectionPos getSectionPos() {
return sectionPos;
}
public byte getDataDetail() {
return (byte) (sectionPos.sectionDetail - SECTION_SIZE_OFFSET);
}
@Override
public byte getDetailOffset() {
return SECTION_SIZE_OFFSET;
}
private CompletableFuture<ColumnRenderBuffer> inBuildRenderBuffer = null;
private Reference<ColumnRenderBuffer> usedBuffer = new Reference<>();
private void tryBuildBuffer(IDhClientLevel level, LodQuadTree quadTree) {
if (inBuildRenderBuffer == null && !ColumnRenderBuffer.isBusy() && !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
if (section != null && section.getRenderSource() != null && section.getRenderSource() instanceof ColumnRenderSource) {
data[direction.ordinal()-2] = ((ColumnRenderSource) section.getRenderSource());
}
}
inBuildRenderBuffer = ColumnRenderBuffer.build(level, usedBuffer, this, data);
}
}
private void cancelBuildBuffer() {
if (inBuildRenderBuffer != null) {
//LOGGER.info("Cancelling build of render buffer for {}", sectionPos);
inBuildRenderBuffer.cancel(true);
inBuildRenderBuffer = null;
}
}
private IDhClientLevel level = null; //FIXME: hack to pass level into tryBuildBuffer
@Override
public void enableRender(IDhClientLevel level, LodQuadTree quadTree) {
this.level = level;
//tryBuildBuffer(level, quadTree);
}
@Override
public void disableRender() {
cancelBuildBuffer();
}
@Override
public void dispose() {
cancelBuildBuffer();
}
//FIXME: Temp Hack
private long lastNs = -1;
private static final long SWAP_TIMEOUT = /* 10 sec */ 10_000_000_000L;
private static final long SWAP_BUSY_COLLISION_TIMEOUT = /* 1 sec */ 1_000_000_000L;
@Override
public boolean trySwapRenderBuffer(LodQuadTree quadTree, AtomicReference<RenderBuffer> referenceSlot) {
if (lastNs != -1 && System.nanoTime() - lastNs < SWAP_TIMEOUT) {
return false;
}
if (inBuildRenderBuffer != null) {
if (inBuildRenderBuffer.isDone()) {
lastNs = System.nanoTime();
//LOGGER.info("Swapping render buffer for {}", sectionPos);
RenderBuffer newBuffer = inBuildRenderBuffer.join();
RenderBuffer oldBuffer = referenceSlot.getAndSet(newBuffer);
if (oldBuffer instanceof ColumnRenderBuffer) {
ColumnRenderBuffer swapped = usedBuffer.swap((ColumnRenderBuffer) oldBuffer);
LodUtil.assertTrue(swapped == null);
}
inBuildRenderBuffer = null;
return true;
}
} else {
if (!isEmpty) {
if (ColumnRenderBuffer.isBusy()) {
lastNs += (long) (SWAP_BUSY_COLLISION_TIMEOUT * Math.random());
} else tryBuildBuffer(level, quadTree);
}
}
return false;
}
@Override
public void saveRender(IDhClientLevel level, RenderMetaFile file, OutputStream dataStream) throws IOException {
DataOutputStream dos = new DataOutputStream(dataStream); // DO NOT CLOSE
writeData(dos);
}
@Override
public byte getRenderVersion() {
return LATEST_VERSION;
}
@Override
public boolean isValid() {
return true;
}
@Override
public boolean isEmpty() {
return isEmpty;
}
public void markNotEmpty() {
isEmpty = false;
}
@Override
public void weakWrite(ILodRenderSource source) {
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) {
int genMode = ColumnFormat.getGenerationMode(dataContainer[i]);
int srcGenMode = ColumnFormat.getGenerationMode(src.dataContainer[i]);
if (srcGenMode == 0) continue;
if (genMode <= srcGenMode) {
new ColumnArrayView(dataContainer, verticalSize, i, verticalSize).copyFrom(
new ColumnArrayView(src.dataContainer, verticalSize, i, verticalSize));
debugSourceFlags[i/verticalSize] = src.debugSourceFlags[i/verticalSize];
}
}
}
@Override
public void fastWrite(ChunkSizedData chunkData, IDhClientLevel level) {
FullToColumnTransformer.writeFullDataChunkToColumnData(this, level, chunkData);
}
public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final boolean DO_SAFETY_CHECKS = true;
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;
public static final long TYPE_ID = "ColumnRenderSource".hashCode();
public static final int AIR_LODS_SIZE = 16;
public static final int AIR_SECTION_SIZE = SECTION_SIZE / AIR_LODS_SIZE;
public final int verticalSize;
public final DhSectionPos sectionPos;
public final int yOffset;
public final long[] dataContainer;
public final int[] airDataContainer;
public final DebugSourceFlag[] debugSourceFlags;
private boolean isEmpty = true;
private IDhClientLevel level = null; //FIXME: hack to pass level into tryBuildBuffer
//FIXME: Temp Hack
private long lastNs = -1;
/** 10 sec */
private static final long SWAP_TIMEOUT = 10_000_000_000L;
/** 1 sec */
private static final long SWAP_BUSY_COLLISION_TIMEOUT = 1_000_000_000L;
private CompletableFuture<ColumnRenderBuffer> inBuildRenderBuffer = null;
private final Reference<ColumnRenderBuffer> usedBuffer = new Reference<>();
/**
* Creates an empty ColumnRenderSource.
*
* @param sectionPos the relative position of the container
* @param maxVerticalSize the maximum vertical size of the container
*/
public ColumnRenderSource(DhSectionPos sectionPos, int maxVerticalSize, int yOffset)
{
verticalSize = maxVerticalSize;
dataContainer = new long[SECTION_SIZE * SECTION_SIZE * verticalSize];
airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE];
this.sectionPos = sectionPos;
this.yOffset = yOffset;
}
/**
* Creates a new ColumnRenderSource with data from the given DataInputStream.
*
* @param inputData Expected format: 1st byte: detail level, 2nd byte: vertical size, 3rd byte on: column data
* @throws IOException if the DataInputStream's detail level isn't what was expected
*/
public ColumnRenderSource(DhSectionPos sectionPos, DataInputStream inputData, int version, IDhLevel level) throws IOException
{
byte detailLevel = inputData.readByte();
if (sectionPos.sectionDetail - SECTION_SIZE_OFFSET != detailLevel)
{
throw new IOException("Invalid data: detail level does not match");
}
this.sectionPos = sectionPos;
this.yOffset = level.getMinY();
this.verticalSize = inputData.readByte() & 0b01111111;
this.dataContainer = 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);
}
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]; }
private long[] loadData(DataInputStream inputData, int version, int verticalSize) throws IOException
{
switch (version)
{
case 1:
return readDataV1(inputData, verticalSize);
default:
throw new IOException("Invalid Data: The version of the data is not supported");
}
}
private long[] readDataV1(DataInputStream inputData, int tempMaxVerticalData) throws IOException
{
int x = 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];
}
isEmpty = false;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
if (tempMinHeight != yOffset)
{
for (int i = 0; i < result.length; i++)
{
result[i] = ColumnFormat.shiftHeightAndDepth(result[i], (short) (tempMinHeight - yOffset));
}
}
return result;
}
@Override
public void clear(int posX, int posZ)
{
for (int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++)
dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex] =
ColumnFormat.EMPTY_DATA;
}
@Override
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex] = data;
return true;
}
@Override
public boolean copyVerticalData(IColumnDataView data, int posX, int posZ, boolean override)
{
if (DO_SAFETY_CHECKS)
{
if (data.size() != verticalSize)
throw new IllegalArgumentException("data size not the same as 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)
{
if (compare < 0)
return false;
}
else
{
if (compare <= 0)
return false;
}
data.copyTo(dataContainer, index, data.size());
return true;
}
@Override
public long getData(int posX, int posZ, int verticalIndex) { return dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex]; }
@Override
public long[] getAllData(int posX, int posZ)
{
long[] result = new long[verticalSize];
int index = posX * SECTION_SIZE * verticalSize + posZ * verticalSize;
System.arraycopy(dataContainer, index, result, 0, verticalSize);
return result;
}
@Override
public ColumnArrayView getVerticalDataView(int posX, int posZ)
{
return new ColumnArrayView(dataContainer, verticalSize,
posX * SECTION_SIZE * verticalSize + posZ * verticalSize, verticalSize);
}
@Override
public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) { return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, quadX, quadZ, quadXSize, quadZSize); }
@Override
public ColumnQuadView getFullQuad() { return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, 0, 0, SECTION_SIZE, SECTION_SIZE); }
@Override
public int getVerticalSize() { return verticalSize; }
@Override
public boolean doesItExist(int posX, int posZ) { return ColumnFormat.doesItExist(getSingleData(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);
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 getMaxNumberOfLods() { return SECTION_SIZE * SECTION_SIZE * getVerticalSize(); }
@Override
public long getRoughRamUsageInBytes() { return (long) dataContainer.length * Long.BYTES; }
public DhSectionPos getSectionPos() { return sectionPos; }
public byte getDataDetail() { return (byte) (sectionPos.sectionDetail - SECTION_SIZE_OFFSET); }
@Override
public byte getDetailOffset() { return SECTION_SIZE_OFFSET; }
private void tryBuildBuffer(IDhClientLevel level, LodQuadTree quadTree)
{
if (inBuildRenderBuffer == null && !ColumnRenderBuffer.isBusy() && !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
if (section != null && section.getRenderSource() != null && section.getRenderSource() instanceof ColumnRenderSource)
{
data[direction.ordinal() - 2] = ((ColumnRenderSource) section.getRenderSource());
}
}
inBuildRenderBuffer = ColumnRenderBuffer.build(level, usedBuffer, this, data);
}
}
private void cancelBuildBuffer()
{
if (inBuildRenderBuffer != null)
{
//LOGGER.info("Cancelling build of render buffer for {}", sectionPos);
inBuildRenderBuffer.cancel(true);
inBuildRenderBuffer = null;
}
}
@Override
public void enableRender(IDhClientLevel level, LodQuadTree quadTree)
{
this.level = level;
//tryBuildBuffer(level, quadTree);
}
@Override
public void disableRender() { cancelBuildBuffer(); }
@Override
public void dispose() { cancelBuildBuffer(); }
@Override
public boolean trySwapRenderBuffer(LodQuadTree quadTree, AtomicReference<RenderBuffer> referenceSlot)
{
if (lastNs != -1 && System.nanoTime() - lastNs < SWAP_TIMEOUT)
{
return false;
}
if (inBuildRenderBuffer != null)
{
if (inBuildRenderBuffer.isDone())
{
lastNs = System.nanoTime();
//LOGGER.info("Swapping render buffer for {}", sectionPos);
RenderBuffer newBuffer = inBuildRenderBuffer.join();
RenderBuffer oldBuffer = referenceSlot.getAndSet(newBuffer);
if (oldBuffer instanceof ColumnRenderBuffer)
{
ColumnRenderBuffer swapped = usedBuffer.swap((ColumnRenderBuffer) oldBuffer);
LodUtil.assertTrue(swapped == null);
}
inBuildRenderBuffer = null;
return true;
}
}
else
{
if (!isEmpty)
{
if (ColumnRenderBuffer.isBusy())
{
lastNs += (long) (SWAP_BUSY_COLLISION_TIMEOUT * Math.random());
}
else
tryBuildBuffer(level, quadTree);
}
}
return false;
}
@Override
public void saveRender(IDhClientLevel level, RenderMetaFile file, OutputStream dataStream) throws IOException
{
DataOutputStream dos = new DataOutputStream(dataStream); // DO NOT CLOSE
writeData(dos);
}
@Override
public byte getRenderVersion() { return LATEST_VERSION; }
@Override
public boolean isValid() { return true; }
@Override
public boolean isEmpty() { return isEmpty; }
public void markNotEmpty() { isEmpty = false; }
@Override
public void weakWrite(ILodRenderSource source)
{
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)
{
int genMode = ColumnFormat.getGenerationMode(dataContainer[i]);
int srcGenMode = ColumnFormat.getGenerationMode(src.dataContainer[i]);
if (srcGenMode == 0)
continue;
if (genMode <= srcGenMode)
{
new ColumnArrayView(dataContainer, verticalSize, i, verticalSize).copyFrom(
new ColumnArrayView(src.dataContainer, verticalSize, i, verticalSize));
debugSourceFlags[i / verticalSize] = src.debugSourceFlags[i / verticalSize];
}
}
}
@Override
public void fastWrite(ChunkSizedData chunkData, IDhClientLevel level) { FullToColumnTransformer.writeFullDataChunkToColumnData(this, level, chunkData); }
//==============//
// helper enums //
//==============//
public enum DebugSourceFlag
{
FULL(ColorUtil.BLUE),
DIRECT(ColorUtil.WHITE),
SPARSE(ColorUtil.YELLOW),
FILE(ColorUtil.BROWN);
public final int color;
DebugSourceFlag(int color) { this.color = color; }
}
}
@@ -1,34 +1,46 @@
package com.seibel.lod.core.datatype.column.accessor;
public interface IColumnDatatype {
byte getDetailOffset();
default int getDataSize() {
return 1 << getDetailOffset();
}
int getMaxNumberOfLods();
long getRoughRamUsage();
int getVerticalSize();
boolean doesItExist(int posX, int posZ);
long getData(int posX, int posZ, int verticalIndex);
default long getSingleData(int posX, int posZ) {return getData(posX, posZ, 0);}
long[] getAllData(int posX, int posZ);
ColumnArrayView getVerticalDataView(int posX, int posZ);
ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize);
ColumnQuadView getFullQuad();
/**
* This method will clear all data at relative section position
*/
void clear(int posX, int posZ);
/**
* This method will add the data given in input at the relative position and vertical index
*/
boolean addData(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
*/
boolean copyVerticalData(IColumnDataView data, int posX, int posZ, boolean override);
void generateData(IColumnDatatype lowerDataContainer, int posX, int posZ);
public interface IColumnDatatype
{
byte getDetailOffset();
default int getDataSize()
{
return 1 << getDetailOffset();
}
int getMaxNumberOfLods();
long getRoughRamUsageInBytes();
int getVerticalSize();
boolean doesItExist(int posX, int posZ);
long getData(int posX, int posZ, int verticalIndex);
default long getSingleData(int posX, int posZ) { return getData(posX, posZ, 0); }
long[] getAllData(int posX, int posZ);
ColumnArrayView getVerticalDataView(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);
/**
* 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
*/
boolean copyVerticalData(IColumnDataView data, int posX, int posZ, boolean override);
void generateData(IColumnDatatype lowerDataContainer, int posX, int posZ);
}
@@ -1,17 +1,21 @@
package com.seibel.lod.core.util.objects;
public class Reference<T> {
public T v;
public Reference() {}
public Reference(T v) {
this.v = v;
}
public T swap(T v) {
T old = this.v;
this.v = v;
return old;
}
public boolean isEmpty() {
return v == null;
}
public class Reference<T>
{
public T value;
public Reference() { }
public Reference(T value) { this.value = value; }
public T swap(T v)
{
T old = this.value;
this.value = v;
return old;
}
public boolean isEmpty() { return value == null; }
}