Basic refactoring
This commit is contained in:
@@ -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; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+43
-31
@@ -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; }
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user