Finish up the ColumnDatatype and related copy and stuff

This commit is contained in:
TomTheFurry
2022-05-17 18:52:50 +08:00
parent e0280cc038
commit 3e5d27505c
15 changed files with 1196 additions and 518 deletions
@@ -30,7 +30,7 @@ public class DHLevel extends LodQuadTree {
MC.getPlayerBlockPos().z);
this.saveFolder = saveFolder;
if (saveFolder != null) {
dataFileHandler = new DataFileHandler(saveFolder);
dataFileHandler = new DataFileHandler(saveFolder, this);
} else {
dataFileHandler = null;
}
@@ -67,4 +67,8 @@ public class DHLevel extends LodQuadTree {
public void render(LodRenderProgram renderContext) {
renderBufferHandler.render(renderContext);
}
public int getMinY() {
return level.getMinHeight();
}
}
@@ -5,6 +5,7 @@ import com.seibel.lod.core.objects.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.objects.a7.pos.DhBlockPos2D;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import com.seibel.lod.core.objects.a7.render.RenderDataSource;
import com.seibel.lod.core.objects.a7.render.RenderDataSourceLoader;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.gridList.MovableGridRingList;
@@ -38,19 +39,20 @@ public abstract class LodQuadTree {
public final byte startingSectionLevel;
private final MovableGridRingList<LodSection>[] ringLists;
static class ContainerTypeConfigEntry {
final Class<? extends RenderDataSource> containerType;
final byte levelOffset;
public ContainerTypeConfigEntry(Class<? extends RenderDataSource> containerType, byte levelOffset) {
this.containerType = containerType;
this.levelOffset = levelOffset;
static final ArrayList<RenderDataSourceLoader> layerLoaderConfig = new ArrayList<>();
public static void registerLayerLoader(RenderDataSourceLoader loader, byte sectionLevel) {
while (layerLoaderConfig.size() <= sectionLevel) {
layerLoaderConfig.add(null);
}
if (layerLoaderConfig.set(sectionLevel, loader) != null) {
throw new RuntimeException("Layer loader for level " + sectionLevel + " has a registry conflict!");
}
}
static final ArrayList<ContainerTypeConfigEntry> containerTypeConfig = new ArrayList<>();
static {
//TODO: Make this dynamic
Collections.addAll(containerTypeConfig,
Collections.addAll(layerLoaderConfig,
null,
null, //1
null, //2
@@ -75,10 +77,10 @@ public abstract class LodQuadTree {
static void assertContainerTypeConfigCorrect() {
boolean isInFront = true;
for (int i = 0; i < containerTypeConfig.size(); i++) {
if (containerTypeConfig.get(i) == null) continue;
for (int i = 0; i < layerLoaderConfig.size(); i++) {
if (layerLoaderConfig.get(i) == null) continue;
isInFront = false;
ContainerTypeConfigEntry entry = containerTypeConfig.get(i);
ContainerTypeConfigEntry entry = layerLoaderConfig.get(i);
if (i - entry.levelOffset < 0) {
throw new RuntimeException("ContainerTypeConfigEntry " + i + " has a levelOffset of "
+ entry.levelOffset + " which makes the dataDetail be " + (i - entry.levelOffset) + "," +
@@ -89,7 +91,7 @@ public abstract class LodQuadTree {
+ entry.levelOffset + " which is less than 0!");
}
}
if (containerTypeConfig.get(containerTypeConfig.size()-1) == null) {
if (layerLoaderConfig.get(layerLoaderConfig.size()-1) == null) {
throw new RuntimeException("The last ContainerTypeConfigEntry is null, which is invalid!");
}
}
@@ -110,14 +112,14 @@ public abstract class LodQuadTree {
ContainerTypeConfigEntry finalEntry = null;
byte topSectionLevel = 0;
byte firstLevel = -1;
for (; topSectionLevel < containerTypeConfig.size(); topSectionLevel++) {
if (containerTypeConfig.get(topSectionLevel) == null) continue;
finalEntry = containerTypeConfig.get(topSectionLevel);
for (; topSectionLevel < layerLoaderConfig.size(); topSectionLevel++) {
if (layerLoaderConfig.get(topSectionLevel) == null) continue;
finalEntry = layerLoaderConfig.get(topSectionLevel);
if (firstLevel == -1) firstLevel = topSectionLevel;
if (topSectionLevel - finalEntry.levelOffset >= maxDetailLevel) break;
}
if (finalEntry == null) throw new RuntimeException("No container type found!");
if (topSectionLevel == containerTypeConfig.size())
if (topSectionLevel == layerLoaderConfig.size())
topSectionLevel = (byte) (maxDetailLevel - finalEntry.levelOffset);
numbersOfSectionLevels = (byte) (topSectionLevel + 1);
startingSectionLevel = firstLevel;
@@ -131,21 +133,21 @@ public abstract class LodQuadTree {
byte targetDataDetail;
Class<? extends RenderDataSource> containerType;
if (i < containerTypeConfig.size()) {
if (containerTypeConfig.get(i) == null) {
if (i < layerLoaderConfig.size()) {
if (layerLoaderConfig.get(i) == null) {
if (lastNonNullEntry == -1) continue;
targetDataDetail = sectionDetailLayers[lastNonNullEntry].targetDataDetail;
containerType = null;
} else {
lastNonNullEntry = i;
ContainerTypeConfigEntry entry = containerTypeConfig.get(i);
ContainerTypeConfigEntry entry = layerLoaderConfig.get(i);
targetDataDetail = (byte) (i - entry.levelOffset);
containerType = entry.containerType;
}
} else {
LodUtil.assertTrue(containerTypeConfig.get(containerTypeConfig.size() - 1) != null,
LodUtil.assertTrue(layerLoaderConfig.get(layerLoaderConfig.size() - 1) != null,
"The last entry must not be null!");
ContainerTypeConfigEntry entry = containerTypeConfig.get(containerTypeConfig.size() - 1);
ContainerTypeConfigEntry entry = layerLoaderConfig.get(layerLoaderConfig.size() - 1);
targetDataDetail = (byte) (i - entry.levelOffset);
containerType = entry.containerType;
}
@@ -2,9 +2,10 @@ package com.seibel.lod.core.objects.a7;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import com.seibel.lod.core.objects.a7.render.RenderDataSource;
import com.seibel.lod.core.objects.a7.render.RenderDataSourceLoader;
import java.util.concurrent.CompletableFuture;
public interface RenderDataProvider {
CompletableFuture<RenderDataSource> createRenderData(RenderDataSource.RenderDataSourceLoader renderSourceLoader, DhSectionPos pos);
CompletableFuture<RenderDataSource> createRenderData(RenderDataSourceLoader renderSourceLoader, DhSectionPos pos);
}
@@ -35,6 +35,7 @@ public class DataFile {
public final File path;
public final DhSectionPos pos;
public final byte dataLevel;
public final LodDataSource.DataSourceLoader loader;
public final Class<?> dataType;
@@ -47,11 +48,12 @@ public class DataFile {
}
}
public DataFile(File path, DhSectionPos pos, LodDataSource.DataSourceLoader loader, Class<?> dataType) {
public DataFile(File path, DhSectionPos pos, LodDataSource.DataSourceLoader loader, Class<?> dataType, byte dataLevel) {
this.path = path;
this.pos = pos;
this.loader = loader;
this.dataType = dataType;
this.dataLevel = dataLevel;
}
DataFile(File path, MappedByteBuffer meta) throws IOException {
@@ -66,7 +68,7 @@ public class DataFile {
int z = meta.getInt();
int checksum = meta.getInt();
byte detailLevel = meta.get();
byte dataDetailLevel = meta.get();
dataLevel = meta.get();
byte loaderVersion = meta.get();
byte unused = meta.get();
long dataTypeId = meta.getLong();
@@ -4,15 +4,15 @@ import com.google.common.collect.HashMultimap;
import com.seibel.lod.core.objects.a7.DHLevel;
import com.seibel.lod.core.objects.a7.RenderDataProvider;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import com.seibel.lod.core.objects.a7.render.EmptyRenderContainer;
import com.seibel.lod.core.objects.a7.render.RenderDataSource;
import com.seibel.lod.core.objects.a7.render.RenderDataSourceLoader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class DataFileHandler implements RenderDataProvider {
public static final String FILE_EXTENSION = ".lod";
@@ -72,16 +72,16 @@ public class DataFileHandler implements RenderDataProvider {
}
@Override
public CompletableFuture<RenderDataSource> createRenderData(RenderDataSource.RenderDataSourceLoader renderSourceLoader, DhSectionPos pos) {
public CompletableFuture<RenderDataSource> createRenderData(RenderDataSourceLoader renderSourceLoader, DhSectionPos pos) {
return CompletableFuture.supplyAsync(() -> {
Set<DataFile> files = renderSourceLoader.selectFiles(pos, level, unloadedDataFileCache.get(pos));
LodDataSource[] dataSource = files.stream().map(f -> {
List<LodDataSource> dataSource = files.stream().map(f -> {
try {
return f.load(level);
} catch (IOException e) {
throw new RuntimeException(e);
}
}).toArray(LodDataSource[]::new);
}).collect(Collectors.toList());
return renderSourceLoader.construct(dataSource, pos, level);
});
}
@@ -0,0 +1,115 @@
package com.seibel.lod.core.objects.a7.datatype.column;
import java.util.Arrays;
import java.util.Iterator;
public final class ColumnArrayView implements ColumnDataView {
private final long[] data;
private final int size; // size in longs
private final int offset; // offset in longs
private final int vertSize; // vertical size in longs
public ColumnArrayView(long[] data, int size, int offset, int vertSize) {
this.data = data;
this.size = size;
this.offset = offset;
this.vertSize = vertSize;
}
@Override
public long get(int index) {
return data[index + offset];
}
public void set(int index, long value) {
data[index + offset] = value;
}
@Override
public int size() {
return size;
}
@Override
public int verticalSize() {
return vertSize;
}
@Override
public int dataCount() {
return size / vertSize;
}
@Override
public ColumnArrayView subView(int dataIndexStart, int dataCount) {
return new ColumnArrayView(data, dataCount * vertSize, offset + dataIndexStart * vertSize, vertSize);
}
public void fill(long value) {
Arrays.fill(data, offset, offset + size, value);
}
public void copyFrom(ColumnDataView source, int outputDataIndexOffset) {
if (source.verticalSize() > vertSize) throw new IllegalArgumentException("source verticalSize must be <= self's verticalSize to copy");
if (source.dataCount() + outputDataIndexOffset > dataCount()) throw new IllegalArgumentException("dataIndexStart + source.dataCount() must be <= self.dataCount() to copy");
if (source.verticalSize() != vertSize) {
for (int i = 0; i < source.dataCount(); i++) {
int outputOffset = offset + outputDataIndexOffset * vertSize + i * vertSize;
source.subView(i, 1).copyTo(data, outputOffset);
Arrays.fill(data, outputOffset + source.verticalSize(),
outputOffset + vertSize, 0);
}
} else {
source.copyTo(data, offset + outputDataIndexOffset * vertSize);
}
}
public void copyFrom(ColumnDataView source) {
copyFrom(source, 0);
}
@Override
public void copyTo(long[] target, int offset) {
System.arraycopy(data, this.offset, target, offset, size);
}
public boolean mergeWith(ColumnArrayView source, boolean override) {
if (size != source.size) {
throw new IllegalArgumentException("Cannot merge views of different sizes");
}
if (vertSize != source.vertSize) {
throw new IllegalArgumentException("Cannot merge views of different vertical sizes");
}
boolean anyChange = false;
for (int o=0; o<(source.size()*vertSize); o+=vertSize) {
if (override) {
if (ColumnDataPoint.compareDatapointPriority(source.get(o), get(o)) >= 0) {
anyChange = true;
System.arraycopy(source.data, source.offset+o, data, offset+o, vertSize);
}
} else {
if (ColumnDataPoint.compareDatapointPriority(source.get(o), get(o)) > 0) {
anyChange = true;
System.arraycopy(source.data, source.offset+o, data, offset+o, vertSize);
}
}
}
return anyChange;
}
public void changeVerticalSizeFrom(ColumnDataView source) {
if (dataCount() != source.dataCount()) {
throw new IllegalArgumentException("Cannot copy and resize to views with different dataCounts");
}
if (vertSize >= source.verticalSize()) {
copyFrom(source);
} else {
for (int i=0; i<dataCount(); i++) {
ColumnDataPoint.mergeMultiData(source.subView(i, 1), subView(i, 1));
}
}
}
public void mergeMultiDataFrom(ColumnDataView source) {
if (dataCount() != 1) {
throw new IllegalArgumentException("output dataCount must be 1");
}
ColumnDataPoint.mergeMultiData(source, this);
}
}
@@ -0,0 +1,617 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.core.objects.a7.datatype.column;
import com.seibel.lod.core.logging.SpamReducedLogger;
import com.seibel.lod.core.util.ColorUtil;
import java.util.Arrays;
/**
*
* @author Leonardo Amato
* @version ??
*/
public class ColumnDataPoint
{
/*
|_ |g |g |g |a |a |a |a |
|r |r |r |r |r |r |r |r |
|g |g |g |g |g |g |g |g |
|b |b |b |b |b |b |b |b |
|h |h |h |h |h |h |h |h |
|h |h |h |h |d |d |d |d |
|d |d |d |d |d |d |d |d |
|bl |bl |bl |bl |sl |sl |sl |sl |
*/
// Reminder: bytes have range of [-128, 127].
// When converting to or from an int a 128 should be added or removed.
// If there is a bug with color then it's probably caused by this.
public final static int EMPTY_DATA = 0;
public final static int MAX_WORLD_Y_SIZE = 4096;
public final static int ALPHA_DOWNSIZE_SHIFT = 4;
public final static int GEN_TYPE_SHIFT = 60;
public final static int COLOR_SHIFT = 32;
public final static int BLUE_SHIFT = COLOR_SHIFT;
public final static int GREEN_SHIFT = BLUE_SHIFT + 8;
public final static int RED_SHIFT = GREEN_SHIFT + 8;
public final static int ALPHA_SHIFT = RED_SHIFT + 8;
public final static int HEIGHT_SHIFT = 20;
public final static int DEPTH_SHIFT = 8;
public final static int BLOCK_LIGHT_SHIFT = 4;
public final static int SKY_LIGHT_SHIFT = 0;
public final static long ALPHA_MASK = 0xF;
public final static long RED_MASK = 0xFF;
public final static long GREEN_MASK = 0xFF;
public final static long BLUE_MASK = 0xFF;
public final static long COLOR_MASK = 0xFFFFFF;
public final static long HEIGHT_MASK = 0xFFF;
public final static long DEPTH_MASK = 0xFFF;
public final static long HEIGHT_DEPTH_MASK = 0xFFFFFF;
public final static long BLOCK_LIGHT_MASK = 0xF;
public final static long SKY_LIGHT_MASK = 0xF;
public final static long GEN_TYPE_MASK = 0b111;
public final static long COMPARE_SHIFT = GEN_TYPE_SHIFT;
public final static long HEIGHT_SHIFTED_MASK = HEIGHT_MASK << HEIGHT_SHIFT;
public final static long DEPTH_SHIFTED_MASK = DEPTH_MASK << DEPTH_SHIFT;
public final static long VOID_SETTER = HEIGHT_SHIFTED_MASK | DEPTH_SHIFTED_MASK;
public static long createVoidDataPoint(byte generationMode)
{
if (generationMode == 0)
throw new IllegalArgumentException("Trying to create void datapoint with genMode 0, which is NOT allowed in DataPoint version 10!");
return (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
}
public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode)
{
return createDataPoint(
ColorUtil.getAlpha(color),
ColorUtil.getRed(color),
ColorUtil.getGreen(color),
ColorUtil.getBlue(color),
height, depth, lightSky, lightBlock, generationMode);
}
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode)
{
if (generationMode == 0)
throw new IllegalArgumentException("Trying to create datapoint with genMode 0, which is NOT allowed in DataPoint version 10!");
if (height < 0 || height > 4096)
throw new IllegalArgumentException("Height must be between 0 and 4096!");
if (depth < 0 || depth > 4096)
throw new IllegalArgumentException("Depth must be between 0 and 4096!");
if (lightSky < 0 || lightSky > 15)
throw new IllegalArgumentException("Sky light must be between 0 and 15!");
if (lightBlock < 0 || lightBlock > 15)
throw new IllegalArgumentException("Block light must be between 0 and 15!");
if (alpha < 0 || alpha > 255)
throw new IllegalArgumentException("Alpha must be between 0 and 255!");
if (red < 0 || red > 255)
throw new IllegalArgumentException("Red must be between 0 and 255!");
if (green < 0 || green > 255)
throw new IllegalArgumentException("Green must be between 0 and 255!");
if (blue < 0 || blue > 255)
throw new IllegalArgumentException("Blue must be between 0 and 255!");
if (generationMode < 0 || generationMode > 7)
throw new IllegalArgumentException("Generation mode must be between 0 and 7!");
if (depth > height)
throw new IllegalArgumentException("Depth must be less than or equal to height!");
return (long) (alpha >>> ALPHA_DOWNSIZE_SHIFT) << ALPHA_SHIFT
| (red & RED_MASK) << RED_SHIFT
| (green & GREEN_MASK) << GREEN_SHIFT
| (blue & BLUE_MASK) << BLUE_SHIFT
| (height & HEIGHT_MASK) << HEIGHT_SHIFT
| (depth & DEPTH_MASK) << DEPTH_SHIFT
| (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT
| (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT
| (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT
;
}
public static long shiftHeightAndDepth(long dataPoint, short offset) {
long height = (dataPoint + ((long) offset << HEIGHT_SHIFT)) & HEIGHT_SHIFTED_MASK;
long depth = (dataPoint + (offset << DEPTH_SHIFT)) & DEPTH_SHIFTED_MASK;
return dataPoint & ~(HEIGHT_SHIFTED_MASK | DEPTH_SHIFTED_MASK) | height | depth;
}
public static long version9Reorder(long dataPoint) {
/*
|a |a |a |a |r |r |r |r |
|r |r |r |r |g |g |g |g |
|g |g |g |g |b |b |b |b |
|b |b |b |b |h |h |h |h |
|h |h |h |h |h |h |d |d |
|d |d |d |d |d |d |d |d |
|bl |bl |bl |bl |sl |sl |sl |sl |
|l |l |f |g |g |g |v |e |
*/
if ((dataPoint & 1) == 0) return 0;
long height = (dataPoint >>> 26) & 0x3FF;
long depth = (dataPoint >>> 16) & 0x3FF;
if (height == depth || (dataPoint & 0b10)==0b10) {
return createVoidDataPoint((byte) (((dataPoint >>> 2) & 0b111) + 1));
}
return ((dataPoint >>> 60) & 0xF) << ALPHA_SHIFT
| ((dataPoint >>> 52) & 0xFF) << RED_SHIFT
| ((dataPoint >>> 44) & 0xFF) << GREEN_SHIFT
| ((dataPoint >>> 36) & 0xFF) << BLUE_SHIFT
| ((dataPoint >>> 26) & 0x3FF) << HEIGHT_SHIFT
| ((dataPoint >>> 16) & 0x3FF) << DEPTH_SHIFT
| ((dataPoint >>> 8) & 0xFF) << SKY_LIGHT_SHIFT
| (((dataPoint >>> 2) & 0xFF) + 1) << GEN_TYPE_SHIFT;
}
public static short getHeight(long dataPoint)
{
return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK);
}
public static short getDepth(long dataPoint)
{
return (short) ((dataPoint >>> DEPTH_SHIFT) & DEPTH_MASK);
}
public static short getAlpha(long dataPoint)
{
return (short) ((((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT) | 0b1111);
}
public static short getRed(long dataPoint)
{
return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK);
}
public static short getGreen(long dataPoint)
{
return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK);
}
public static short getBlue(long dataPoint)
{
return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK);
}
public static byte getLightSky(long dataPoint)
{
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
}
public static byte getLightBlock(long dataPoint)
{
return (byte) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
}
private static final SpamReducedLogger warnLogger = new SpamReducedLogger(1);
public static byte getGenerationMode(long dataPoint)
{
byte genMode = (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
if (warnLogger.canMaybeLog() && doesItExist(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;
}
return genMode == 0 ? 1 : genMode;
}
public static boolean isVoid(long dataPoint)
{
return (((dataPoint >>> DEPTH_SHIFT) & HEIGHT_DEPTH_MASK) == 0);
}
public static boolean doesItExist(long dataPoint)
{
return dataPoint!=0;
}
public static int getColor(long dataPoint)
{
long alpha = getAlpha(dataPoint);
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (alpha << (ALPHA_SHIFT-COLOR_SHIFT)));
}
/** This is used to convert a dataPoint to string (useful for the print function) */
@SuppressWarnings("unused")
public static String toString(long dataPoint)
{
return getHeight(dataPoint) + " " +
getDepth(dataPoint) + " " +
getAlpha(dataPoint) + " " +
getRed(dataPoint) + " " +
getBlue(dataPoint) + " " +
getGreen(dataPoint) + " " +
getLightBlock(dataPoint) + " " +
getLightSky(dataPoint) + " " +
getGenerationMode(dataPoint) + " " +
isVoid(dataPoint) + " " +
doesItExist(dataPoint) + '\n';
}
private static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
//remove comment to not leave garbage at the end
//array[start + packetSize + i] = 0;
if (arraySize - start >= 0) System.arraycopy(array, start + length, array, start, arraySize - start);
}
private static void extendArray(short[] array, int packetSize, int start, int length, int arraySize)
{
start *= packetSize;
length *= packetSize;
arraySize *= packetSize;
for (int i = arraySize - start - 1; i >= 0; i--)
{
array[start + length + i] = array[start + i];
array[start + i] = 0;
}
}
/** Return (>0) if dataA should replace dataB, (0) if equal, (<0) if dataB should replace dataA */
public static int compareDatapointPriority(long dataA, long dataB) {
return (int) ((dataA >> COMPARE_SHIFT) - (dataB >> COMPARE_SHIFT));
}
private static final ThreadLocal<short[]> tLocalHeightAndDepth = new ThreadLocal<short[]>();
private static final ThreadLocal<int[]> tDataIndexCache = new ThreadLocal<int[]>();
/**
* This method merge column of multiple data together
* @param sourceData one or more columns of data
* @param output one column of space for the result to be written to
*/
public static void mergeMultiData(ColumnDataView sourceData, ColumnArrayView output)
{
if (output.dataCount() != 1) throw new IllegalArgumentException("output must be only reserved for one datapoint!");
int inputVerticalSize = sourceData.verticalSize();
int outputVerticalSize = output.verticalSize();
output.fill(0);
//dataCount indicate how many position we are merging in one position
int dataCount = sourceData.dataCount();
// We initialize the arrays that are going to be used
int heightAndDepthLength = (MAX_WORLD_Y_SIZE / 2 + 16) * 2;
short[] heightAndDepth = tLocalHeightAndDepth.get();
if (heightAndDepth==null || heightAndDepth.length != heightAndDepthLength) {
heightAndDepth = new short[heightAndDepthLength];
tLocalHeightAndDepth.set(heightAndDepth);
}
byte genMode = getGenerationMode(sourceData.get(0));
if (genMode == 0) genMode = 1; // FIXME: Hack to make the version 10 genMode never be 0.
boolean allEmpty = true;
boolean allVoid = true;
boolean limited = false;
boolean allDefault;
long singleData;
short depth;
short height;
int count = 0;
int i;
int ii;
int dataIndex;
//We collect the indexes of the data, ordered by the depth
for (int index = 0; index < dataCount; index++)
{
if (index == 0)
{
for (dataIndex = 0; dataIndex < inputVerticalSize; dataIndex++)
{
singleData = sourceData.get(dataIndex);
if (doesItExist(singleData))
{
//genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!isVoid(singleData))
{
allVoid = false;
count++;
heightAndDepth[dataIndex * 2] = getHeight(singleData);
heightAndDepth[dataIndex * 2 +1] = getDepth(singleData);
}
}
else
break;
}
}
else
{
for (dataIndex = 0; dataIndex < inputVerticalSize; dataIndex++)
{
singleData = sourceData.get(index * inputVerticalSize + dataIndex);
if (doesItExist(singleData))
{
//genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!isVoid(singleData))
{
allVoid = false;
depth = getDepth(singleData);
height = getHeight(singleData);
int botPos = -1;
int topPos = -1;
//values fall in between and possibly require extension of array
boolean botExtend = false;
boolean topExtend = false;
for (i = 0; i < count; i++)
{
if (depth < heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1])
{
botPos = i;
break;
}
else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth >= heightAndDepth[(i + 1) * 2]) || i + 1 == count))
{
botPos = i;
botExtend = true;
break;
}
}
for (i = 0; i < count; i++)
{
if (height <= heightAndDepth[i * 2] && height > heightAndDepth[i * 2 + 1])
{
topPos = i;
break;
}
else if (height <= heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
{
topPos = i;
topExtend = true;
break;
}
}
if (topPos == -1)
{
if (botPos == -1)
{
//whole block falls above
extendArray(heightAndDepth, 2, 0, 1, count);
heightAndDepth[0] = height;
heightAndDepth[1] = depth;
count++;
}
else if (!botExtend)
{
//only top falls above extending it there, while bottom is inside existing
shrinkArray(heightAndDepth, 2, 0, botPos, count);
heightAndDepth[0] = height;
count -= botPos;
}
else
{
//top falls between some blocks, extending those as well
shrinkArray(heightAndDepth, 2, 0, botPos, count);
heightAndDepth[0] = height;
heightAndDepth[1] = depth;
count -= botPos;
}
}
else if (!topExtend)
{
if (!botExtend)
//both top and bottom are within some exiting blocks, possibly merging them
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
else
//top falls between some blocks, extending it there
heightAndDepth[topPos * 2 + 1] = depth;
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
}
else
{
if (!botExtend)
{
//only top is within some exiting block, extending it
topPos++; //to make it easier
heightAndDepth[topPos * 2] = height;
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
}
else
{
//both top and bottom are outside existing blocks
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
extendArray(heightAndDepth, 2, topPos + 1, 1, count);
count++;
heightAndDepth[topPos * 2 + 2] = height;
heightAndDepth[topPos * 2 + 3] = depth;
}
}
}
}
else
break;
}
}
}
//We check if there is any data that's not empty or void
if (allEmpty) {
return;
}
if (allVoid)
{
output.set(0,createVoidDataPoint(genMode));
return;
}
//we limit the vertical portion to maxVerticalData
int j = 0;
while (count > outputVerticalSize)
{
limited = true;
ii = MAX_WORLD_Y_SIZE;
for (i = 0; i < count - 1; i++)
{
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii)
{
ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2];
j = i;
}
}
heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1];
for (i = j + 1; i < count - 1; i++)
{
heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2];
heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1];
}
//System.arraycopy(heightAndDepth, j + 1, heightAndDepth, j, count - j - 1);
count--;
}
//As standard the vertical lods are ordered from top to bottom
if (!limited && dataCount == 1) // This mean source vertSize < output vertSize AND both dataCount == 1
{
output.copyFrom(sourceData);
}
else
{
//We want to efficiently memorize indexes
int[] dataIndexesCache = tDataIndexCache.get();
if (dataIndexesCache==null || dataIndexesCache.length != dataCount) {
dataIndexesCache = new int[dataCount];
tDataIndexCache.set(dataIndexesCache);
}
Arrays.fill(dataIndexesCache,0);
//For each lod height-depth value we have found we now want to generate the rest of the data
//by merging all lods at lower level that are contained inside the new ones
for (j = 0; j < count; j++)
{
//We firstly collect height and depth data
//this will be added to each realtive long DataPoint
height = heightAndDepth[j * 2];
depth = heightAndDepth[j * 2 + 1];
//if both height and depth are at 0 then we finished
if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2)
break;
//We initialize data useful for the merge
int numberOfChildren = 0;
allEmpty = true;
allVoid = true;
//We initialize all the new values that we are going to put in the dataPoint
int tempAlpha = 0;
int tempRed = 0;
int tempGreen = 0;
int tempBlue = 0;
int tempLightBlock = 0;
int tempLightSky = 0;
long data = 0;
//For each position that we want to merge
for (int index = 0; index < dataCount; index++)
{
//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))
{
dataIndexesCache[index]++;
if ((depth <= getDepth(singleData) && getDepth(singleData) < height)
|| (depth < getHeight(singleData) && getHeight(singleData) <= height))
{
data = singleData;
break;
}
}
else
break;
}
if (!doesItExist(data))
{
data = createVoidDataPoint(genMode);
}
if (doesItExist(data))
{
allEmpty = false;
if (!isVoid(data))
{
numberOfChildren++;
allVoid = false;
tempAlpha = Math.max(getAlpha(data),tempAlpha);
tempRed += getRed(data) * getRed(data);
tempGreen += getGreen(data) * getGreen(data);
tempBlue += getBlue(data) * getBlue(data);
tempLightBlock += getLightBlock(data);
tempLightSky += getLightSky(data);
}
}
}
if (allEmpty)
//no child has been initialized
output.set(j, EMPTY_DATA);
else if (allVoid)
//all the children are void
output.set(j, createVoidDataPoint(genMode));
else
{
//we have at least 1 child
if (dataCount != 1)
{
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempLightBlock = tempLightBlock / numberOfChildren;
tempLightSky = tempLightSky / numberOfChildren;
}
//data = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
//if (j > 0 && getColor(data) == getColor(dataPoint[j]))
//{
// add simplification at the end due to color
//}
output.set(j, createDataPoint((int) Math.sqrt(tempAlpha), (int) Math.sqrt(tempRed), (int) Math.sqrt(tempGreen), (int) Math.sqrt(tempBlue), height, depth, tempLightSky, tempLightBlock, genMode));
}
}
}
}
}
@@ -0,0 +1,33 @@
package com.seibel.lod.core.objects.a7.datatype.column;
import java.util.Iterator;
public interface ColumnDataView {
long get(int index);
int size();
default Iterator<Long> iterator() {
return new Iterator<Long>() {
private int index = 0;
private final int size = size();
@Override
public boolean hasNext() {
return index < size;
}
@Override
public Long next() {
return get(index++);
}
};
}
int verticalSize();
int dataCount();
ColumnDataView subView(int dataIndexStart, int dataCount);
void copyTo(long[] target, int offset);
}
@@ -2,9 +2,15 @@ package com.seibel.lod.core.objects.a7.datatype.column;
import com.seibel.lod.core.objects.LodDataView;
import com.seibel.lod.core.objects.a7.DHLevel;
import com.seibel.lod.core.objects.a7.LodQuadTree;
import com.seibel.lod.core.objects.a7.data.DataFile;
import com.seibel.lod.core.objects.a7.data.LodDataSource;
import com.seibel.lod.core.objects.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.objects.a7.pos.DhLodPos;
import com.seibel.lod.core.objects.a7.pos.DhLodUnit;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import com.seibel.lod.core.objects.a7.render.RenderDataSource;
import com.seibel.lod.core.objects.a7.render.RenderDataSourceLoader;
import com.seibel.lod.core.objects.opengl.RenderBuffer;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.DetailDistanceUtil;
@@ -16,7 +22,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
public class ColumnDatatype implements LodDataSource, RenderDataSource {
@@ -24,10 +30,13 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
public static final byte SECTION_SIZE_OFFSET = 6;
public static final int SECTION_SIZE = 1 << SECTION_SIZE_OFFSET;
public static final int LATEST_VERSION = 9;
public static final long DATA_TYPE_ID = "ColumnDatatype".hashCode();
public final int AIR_LODS_SIZE = 16;
public 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;
@@ -36,16 +45,18 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
* Constructor of the ColumnDataType
* @param maxVerticalSize the maximum vertical size of the container
*/
public ColumnDatatype(DhSectionPos sectionPos, int maxVerticalSize) {
public ColumnDatatype(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];
this.sectionPos = sectionPos;
this.yOffset = yOffset;
}
// Load from data stream with maxVerticalSize loaded from the data stream
public ColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version) throws IOException {
public ColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, DHLevel level) throws IOException {
this.sectionPos = sectionPos;
this.yOffset = level.getMinY();
byte detailLevel = inputData.readByte();
if (sectionPos.sectionDetail - SECTION_SIZE_OFFSET != detailLevel) {
throw new IOException("Invalid data: detail level does not match");
@@ -53,16 +64,16 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
verticalSize = inputData.readByte() & 0b01111111;
switch (version) {
case 6:
dataContainer = readDataVersion6(inputData, verticalSize, sectionPos.yOffset);
dataContainer = readDataVersion6(inputData, verticalSize);
break;
case 7:
dataContainer = readDataVersion7(inputData, verticalSize, sectionPos.yOffset);
dataContainer = readDataVersion7(inputData, verticalSize);
break;
case 8:
dataContainer = readDataVersion8(inputData, verticalSize, sectionPos.yOffset);
dataContainer = readDataVersion8(inputData, verticalSize);
break;
case 9:
dataContainer = readDataVersion9(inputData, verticalSize, sectionPos.yOffset);
dataContainer = readDataVersion9(inputData, verticalSize);
break;
default:
throw new IOException("Invalid Data: The version of the data is not supported");
@@ -71,8 +82,9 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
}
// Load from data stream with new maxVerticalSize
public ColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, int maxVerticalSize) throws IOException {
public ColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, DHLevel level, int maxVerticalSize) throws IOException {
verticalSize = maxVerticalSize;
this.yOffset = level.getMinY();
this.sectionPos = sectionPos;
byte detailLevel = inputData.readByte();
if (sectionPos.sectionDetail - SECTION_SIZE_OFFSET != detailLevel) {
@@ -82,16 +94,16 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
long[] fileDataContainer = null;
switch (version) {
case 6:
fileDataContainer = readDataVersion6(inputData, fileMaxVerticalSize, sectionPos.yOffset);
fileDataContainer = readDataVersion6(inputData, fileMaxVerticalSize);
break;
case 7:
fileDataContainer = readDataVersion7(inputData, fileMaxVerticalSize, sectionPos.yOffset);
fileDataContainer = readDataVersion7(inputData, fileMaxVerticalSize);
break;
case 8:
fileDataContainer = readDataVersion8(inputData, fileMaxVerticalSize, sectionPos.yOffset);
fileDataContainer = readDataVersion8(inputData, fileMaxVerticalSize);
break;
case 9:
fileDataContainer = readDataVersion9(inputData, fileMaxVerticalSize, sectionPos.yOffset);
fileDataContainer = readDataVersion9(inputData, fileMaxVerticalSize);
break;
default:
throw new IOException("Invalid Data: The version of the data is not supported");
@@ -100,30 +112,6 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
}
// Copy constructor
public ColumnDatatype(DhSectionPos sectionPos, LodDataSource dataSource, int maxVerticalData) {
verticalSize = maxVerticalData;
this.sectionPos = sectionPos;
dataContainer = new long[SECTION_SIZE * SECTION_SIZE * verticalSize];
airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
DhSectionPos sourcePos = dataSource.getSectionPos();
if (!sourcePos.overlaps(sectionPos)) {
throw new IllegalArgumentException("The source section does not overlap with new target position");
}
if (sourcePos.dataDetail > sectionPos.dataDetail) {
throw new IllegalArgumentException("The source section has higher detail than new target detail");
}
if (sourcePos.yOffset != sectionPos.yOffset) {
throw new IllegalArgumentException("Different yOffset is not yet supported"); // TODO: is this needed?
}
if (sourcePos.equals(sectionPos)) {
//TODO: Simple full copy.
} else {
//TODO: Downsample copy.
}
}
/**
* This method will clear all data at relative section position
* @param posX
@@ -256,8 +244,16 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return result;
}
public LodDataView getVerticalDataView(int posX, int posZ) {
return new LodDataView(dataContainer, verticalSize, posX * SECTION_SIZE * verticalSize + posZ * verticalSize);
public ColumnArrayView getVerticalDataView(int posX, int posZ) {
return new ColumnArrayView(dataContainer, verticalSize,
posX * SECTION_SIZE * verticalSize + posZ * verticalSize, verticalSize);
}
public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) {
return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, quadX, quadZ, quadXSize, quadZSize);
}
public ColumnQuadView getFullQuad() {
return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, 0, 0, SECTION_SIZE, SECTION_SIZE);
}
public int getVerticalSize()
@@ -270,7 +266,7 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return DataPointUtil.doesItExist(getSingleData(posX, posZ));
}
private long[] readDataVersion6(DataInputStream inputData, int tempMaxVerticalData, int yOffset) throws IOException {
private long[] readDataVersion6(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
@@ -281,7 +277,7 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
patchHeightAndDepth(result,-yOffset);
return result;
}
private long[] readDataVersion7(DataInputStream inputData, int tempMaxVerticalData, int yOffset) throws IOException {
private long[] readDataVersion7(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
@@ -293,7 +289,7 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return result;
}
private long[] readDataVersion8(DataInputStream inputData, int tempMaxVerticalData, int yOffset) throws IOException {
private long[] readDataVersion8(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
@@ -308,7 +304,7 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return result;
}
private long[] readDataVersion9(DataInputStream inputData, int tempMaxVerticalData, int yOffset) throws IOException {
private long[] readDataVersion9(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
@@ -339,60 +335,19 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return new long[LodUtil.DETAIL_OPTIONS - 1][];
});
public void updateData(ColumnDatatype lowerDataContainer, int posX, int posZ)
public void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ)
{
//We reset the array
long[][] verticalUpdateArrays = tLocalVerticalUpdateArrays.get();
long[] dataToMerge = verticalUpdateArrays[sectionPos.dataDetail -1];
int arrayLength = DetailDistanceUtil.getMaxVerticalData(sectionPos.dataDetail -1) * 4;
if (dataToMerge == null || dataToMerge.length != arrayLength) {
dataToMerge = new long[arrayLength];
verticalUpdateArrays[sectionPos.dataDetail -1] = dataToMerge;
} else Arrays.fill(dataToMerge, 0);
//int lowerMaxVertical = dataToMerge.length / 4;
int lowerSectionSize = lowerDataContainer.getSECTION_SIZE();
int childPosStartX = Math.floorMod(2 * posX, lowerSectionSize);
int childPosEndX = Math.floorMod(2 * posX + 1, lowerSectionSize);
int childPosStartZ = Math.floorMod(2 * posZ, lowerSectionSize);
int childPosEndZ = Math.floorMod(2 * posZ + 1, lowerSectionSize);
long[] data;
boolean anyDataExist = false;
mergeAndAddDataFromOtherContainer(posX, posZ, lowerDataContainer, childPosStartX, childPosEndX, childPosStartZ, childPosEndZ);
/*
TODO remove this old code when we are sure that this works
for (int x = 0; x <= 1; x++)
{
for (int z = 0; z <= 1; z++)
{
childPosX = 2 * posX + x;
childPosZ = 2 * posZ + z;
if (lowerLevelContainer.doesItExist(childPosX, childPosZ)) anyDataExist = true;
for (int verticalIndex = 0; verticalIndex < lowerMaxVertical; verticalIndex++)
dataToMerge[(z * 2 + x) * lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
}
}
if (!anyDataExist)
throw new RuntimeException("Update data called but no child datapoint exist!");
if ((!DataPointUtil.doesItExist(data[0])) && anyDataExist)
throw new RuntimeException("Update data called but higher level datapoint doesn't exist even though child data does exist!");
//FIXME: Disabled check if genMode for old data is already invalid due to having genMode 0.
if (DataPointUtil.getGenerationMode(data[0]) != DataPointUtil.getGenerationMode(lowerLevelContainer.getSingleData(posX*2, posZ*2)))
throw new RuntimeException("Update data called but higher level datapoint does not have the same GenerationMode as the top left corner child datapoint!");
forceWriteVerticalData(data, posX, posZ);*/
ColumnQuadView quadView = lowerDataContainer.getDataInQuad(posX*2, posZ*2, 2,2);
ColumnArrayView outputView = getVerticalDataView(posX, posZ);
outputView.mergeMultiDataFrom(quadView);
}
public boolean writeData(DataOutputStream output) throws IOException {
output.writeByte(sectionPos.dataDetail);
output.writeByte(getDataDetail());
output.writeByte((byte) verticalSize);
// FIXME: yOffset is a int, but we only are writing a short.
output.writeByte((byte) (sectionPos.yOffset & 0xFF));
output.writeByte((byte) ((sectionPos.yOffset >> 8) & 0xFF));
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++)
@@ -443,401 +398,186 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return (long) dataContainer.length * Long.BYTES;
}
private static final ThreadLocal<short[]> tLocalHeightAndDepth = new ThreadLocal<short[]>();
private static final ThreadLocal<int[]> tDataIndexCache = new ThreadLocal<int[]>();
/**
*
* This method merge column of multiple data together
*/
// TODO: Make this operate on a out param array, to allow skipping copy array on use
public void mergeAndAddDataFromOtherContainer(int mergeInX, int mergeInZ, ColumnDatatype lowerDataContainer, int mergeFromX, int mergeToX, int mergeFromZ, int mergeToZ)
{
int outBaseIndex = mergeInX * SECTION_SIZE * verticalSize + mergeInZ*verticalSize;
int inputVerticalSize = lowerDataContainer.verticalSize;
int inputSectionSize = lowerDataContainer.SECTION_SIZE;
int mergeFromToX;
int xSize = (mergeFromX - mergeToX + 1);
int zSize = (mergeFromZ - mergeToZ + 1);
//size indicate how many position we are merging in one position
// We initialize the arrays that are going to be used
int heightAndDepthLength = (DataPointUtil.MAX_WORLD_Y_SIZE / 2 + 16) * 2;
short[] heightAndDepth = tLocalHeightAndDepth.get();
if (heightAndDepth==null || heightAndDepth.length != heightAndDepthLength) {
heightAndDepth = new short[heightAndDepthLength];
tLocalHeightAndDepth.set(heightAndDepth);
}
int dataPointLength = verticalSize;
int firstIndex = mergeFromX*SECTION_SIZE*inputVerticalSize + mergeFromZ * inputVerticalSize;
byte genMode = DataPointUtil.getGenerationMode(lowerDataContainer.dataContainer[firstIndex]);
if (genMode == 0) genMode = 1; // FIXME: Hack to make the version 10 genMode never be 0.
boolean allEmpty = true;
boolean allVoid = true;
boolean limited = false;
boolean allDefault;
long singleData;
short depth;
short height;
int count = 0;
int i;
int ii;
//We collect the indexes of the data, ordered by the depth
int dataIndex = 0;
int x;
int z;
int y;
for (x = mergeFromX; x <= mergeToX; x++)
{
for (z = mergeFromZ; z <= mergeToZ; z++)
{
if (x == mergeFromX && z == mergeFromZ)
{
for (y = 0; y < inputVerticalSize; y++)
{
dataIndex = x * inputSectionSize * inputVerticalSize + z * inputVerticalSize + y;
singleData = lowerDataContainer.dataContainer[dataIndex];
if (DataPointUtil.doesItExist(singleData))
{
//genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!DataPointUtil.isVoid(singleData))
{
allVoid = false;
count++;
heightAndDepth[dataIndex * 2] = DataPointUtil.getHeight(singleData);
heightAndDepth[dataIndex * 2 + 1] = DataPointUtil.getDepth(singleData);
}
}
else
break;
}
}
else
{
for (y = 0; y < inputVerticalSize; y++)
{
dataIndex = x * inputSectionSize * inputVerticalSize + z * inputVerticalSize + y;
singleData = lowerDataContainer.dataContainer[dataIndex];
if (DataPointUtil.doesItExist(singleData))
{
//genMode = Math.min(genMode, getGenerationMode(singleData));
allEmpty = false;
if (!DataPointUtil.isVoid(singleData))
{
allVoid = false;
depth = DataPointUtil.getDepth(singleData);
height = DataPointUtil.getHeight(singleData);
int botPos = -1;
int topPos = -1;
//values fall in between and possibly require extension of array
boolean botExtend = false;
boolean topExtend = false;
for (i = 0; i < count; i++)
{
if (depth < heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1])
{
botPos = i;
break;
}
else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth >= heightAndDepth[(i + 1) * 2]) || i + 1 == count))
{
botPos = i;
botExtend = true;
break;
}
}
for (i = 0; i < count; i++)
{
if (height <= heightAndDepth[i * 2] && height > heightAndDepth[i * 2 + 1])
{
topPos = i;
break;
}
else if (height <= heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
{
topPos = i;
topExtend = true;
break;
}
}
if (topPos == -1)
{
if (botPos == -1)
{
//whole block falls above
DataPointUtil.extendArray(heightAndDepth, 2, 0, 1, count);
heightAndDepth[0] = height;
heightAndDepth[1] = depth;
count++;
}
else if (!botExtend)
{
//only top falls above extending it there, while bottom is inside existing
DataPointUtil.shrinkArray(heightAndDepth, 2, 0, botPos, count);
heightAndDepth[0] = height;
count -= botPos;
}
else
{
//top falls between some blocks, extending those as well
DataPointUtil.shrinkArray(heightAndDepth, 2, 0, botPos, count);
heightAndDepth[0] = height;
heightAndDepth[1] = depth;
count -= botPos;
}
}
else if (!topExtend)
{
if (!botExtend)
//both top and bottom are within some exiting blocks, possibly merging them
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
else
//top falls between some blocks, extending it there
heightAndDepth[topPos * 2 + 1] = depth;
DataPointUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
}
else
{
if (!botExtend)
{
//only top is within some exiting block, extending it
topPos++; //to make it easier
heightAndDepth[topPos * 2] = height;
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
DataPointUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
}
else
{
//both top and bottom are outside existing blocks
DataPointUtil.shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
count -= botPos - topPos;
DataPointUtil.extendArray(heightAndDepth, 2, topPos + 1, 1, count);
count++;
heightAndDepth[topPos * 2 + 2] = height;
heightAndDepth[topPos * 2 + 3] = depth;
}
}
}
}
else
break;
}
}
}
}
//We check if there is any data that's not empty or void
if (allEmpty)
return;
if (allVoid)
{
dataContainer[outBaseIndex] = DataPointUtil.createVoidDataPoint(genMode);
return;
}
//we limit the vertical portion to maxVerticalData
int j = 0;
while (count > verticalSize)
{
limited = true;
ii = DataPointUtil.MAX_WORLD_Y_SIZE;
for (i = 0; i < count - 1; i++)
{
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii)
{
ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2];
j = i;
}
}
heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1];
for (i = j + 1; i < count - 1; i++)
{
heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2];
heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1];
}
//System.arraycopy(heightAndDepth, j + 1, heightAndDepth, j, count - j - 1);
count--;
}
int yOut;
//As standard the vertical lods are ordered from top to bottom
if (!limited && xSize*zSize == 1)
{
for (yOut = 0; yOut < count; yOut++)
dataIndex = mergeFromX*inputSectionSize*inputVerticalSize + mergeFromZ*inputVerticalSize + yOut;
dataContainer[outBaseIndex + yOut] = lowerDataContainer.dataContainer[dataIndex];
}
else
{
//We want to efficiently memorize indexes
int[] dataIndexesCache = tDataIndexCache.get();
if (dataIndexesCache==null || dataIndexesCache.length != xSize*zSize) {
dataIndexesCache = new int[xSize*zSize];
tDataIndexCache.set(dataIndexesCache);
}
Arrays.fill(dataIndexesCache,0);
//For each lod height-depth value we have found we now want to generate the rest of the data
//by merging all lods at lower level that are contained inside the new ones
for (yOut = 0; yOut < count; yOut++)
{
//We firstly collect height and depth data
//this will be added to each realtive long DataPoint
height = heightAndDepth[yOut * 2];
depth = heightAndDepth[yOut * 2 + 1];
//if both height and depth are at 0 then we finished
if ((depth == 0 && height == 0) || yOut >= heightAndDepth.length / 2)
break;
//We initialize data useful for the merge
int numberOfChildren = 0;
allEmpty = true;
allVoid = true;
//We initialize all the new values that we are going to put in the dataPoint
int tempAlpha = 0;
int tempRed = 0;
int tempGreen = 0;
int tempBlue = 0;
int tempLightBlock = 0;
int tempLightSky = 0;
long data = 0;
int index;
//For each position that we want to merge
for(x = mergeFromX; x <= mergeToX; x++)
{
for (z = mergeFromZ; z <= mergeToZ; z++)
{
index = x * xSize + z;
//we scan the lods in the position from top to bottom
while (dataIndexesCache[index] < inputVerticalSize)
{
y = dataIndexesCache[index];
dataIndex = x * inputSectionSize * inputVerticalSize + z * inputVerticalSize + y;
singleData = lowerDataContainer.dataContainer[dataIndex];
if (DataPointUtil.doesItExist(singleData) && !DataPointUtil.isVoid(singleData))
{
dataIndexesCache[index]++;
if ((depth <= DataPointUtil.getDepth(singleData) && DataPointUtil.getDepth(singleData) < height)
|| (depth < DataPointUtil.getHeight(singleData) && DataPointUtil.getHeight(singleData) <= height))
{
data = singleData;
break;
}
}
else
break;
}
if (!DataPointUtil.doesItExist(data))
{
data = DataPointUtil.createVoidDataPoint(genMode);
}
if (DataPointUtil.doesItExist(data))
{
allEmpty = false;
if (!DataPointUtil.isVoid(data))
{
numberOfChildren++;
allVoid = false;
tempAlpha = Math.max(DataPointUtil.getAlpha(data), tempAlpha);
tempRed += DataPointUtil.getRed(data) * DataPointUtil.getRed(data);
tempGreen += DataPointUtil.getGreen(data) * DataPointUtil.getGreen(data);
tempBlue += DataPointUtil.getBlue(data) * DataPointUtil.getBlue(data);
tempLightBlock += DataPointUtil.getLightBlock(data);
tempLightSky += DataPointUtil.getLightSky(data);
}
}
}
}
if (allEmpty)
//no child has been initialized
dataContainer[outBaseIndex + yOut] = DataPointUtil.EMPTY_DATA;
else if (allVoid)
//all the children are void
dataContainer[outBaseIndex + yOut] = DataPointUtil.createVoidDataPoint(genMode);
else
{
//we have at least 1 child
if (xSize*zSize != 1)
{
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempLightBlock = tempLightBlock / numberOfChildren;
tempLightSky = tempLightSky / numberOfChildren;
}
//data = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
//if (j > 0 && getColor(data) == getColor(dataPoint[j]))
//{
// add simplification at the end due to color
//}
dataContainer[outBaseIndex + yOut] = DataPointUtil.createDataPoint((int) Math.sqrt(tempAlpha), (int) Math.sqrt(tempRed), (int) Math.sqrt(tempGreen), (int) Math.sqrt(tempBlue), height, depth, tempLightSky, tempLightBlock, genMode);
}
}
}
}
public static LodDataSource load(DhSectionPos pos, InputStream is, int version) {
public static LodDataSource loadFile(DHLevel level, DhSectionPos pos, InputStream is, int version) {
try (DataInputStream dis = new DataInputStream(is)) {
return new ColumnDatatype(pos, dis, version);
return new ColumnDatatype(pos, dis, version, level);
} catch (IOException e) {
//FIXME: Log error
return null;
}
}
public static class ColumnRenderSourceLoader extends RenderDataSourceLoader {
@Override
public RenderDataSource construct(LodDataSource[] dataSources, DhSectionPos sectionPos, DHLevel level) {
// Select the direct one first
for (LodDataSource dataSource : dataSources) {
if (dataSource instanceof ColumnDatatype) {
return (RenderDataSource) dataSource;
public static RenderDataSourceLoader COLUMN_LAYER_LOADER = new RenderDataSourceLoader(4) {
@Override
public RenderDataSource construct(List<LodDataSource> dataSources, DhSectionPos sectionPos, DHLevel level) {
if (dataSources.size() == 0) return null;
// Check for direct casting
if (dataSources.size() == 1 && dataSources.get(0) instanceof ColumnDatatype
&& dataSources.get(0).getSectionPos().equals(sectionPos)
&& dataSources.get(0).getDataDetail() == sectionPos.sectionDetail-SECTION_SIZE_OFFSET) {
// Directly using the data source as the render data source is possible.
return (ColumnDatatype) dataSources.get(0);
}
// Otherwise, we need to create a new render data source, and copy the data from the data sources.
ColumnDatatype renderDataSource = new ColumnDatatype(sectionPos,
DetailDistanceUtil.getMaxVerticalData(sectionPos.sectionDetail-SECTION_SIZE_OFFSET),
level.getMinY());
boolean completeCopy = dataSources.get(0).getSectionPos().getWidth().toBlock() >= sectionPos.getWidth().toBlock();
if (completeCopy) {
// If there is only one data source, we need to insure on copy, we don't copy out of bounds as we
// may just need to copy partial section of the data source.
LodUtil.assertTrue(dataSources.size() == 1, "Expected only one data source for complete copy");
byte targetDataLevel = (byte) (sectionPos.sectionDetail-SECTION_SIZE_OFFSET);
byte sourceDataLevel = dataSources.get(0).getDataDetail();
LodUtil.assertTrue(targetDataLevel >= sourceDataLevel);
if (dataSources.get(0) instanceof ColumnDatatype) {
ColumnDatatype dataSource = (ColumnDatatype) dataSources.get(0);
DhSectionPos srcPos = dataSource.getSectionPos();
// Note that in here, the source data level will be always < target section level
int trgX = sectionPos.getCorner().getX().toBlock();
int trgZ = sectionPos.getCorner().getZ().toBlock();
int trgMaxX = trgX + sectionPos.getWidth().toBlock() - 1;
int trgMaxZ = trgZ + sectionPos.getWidth().toBlock() - 1;
int trgXSizeInSrc = (trgX >> sourceDataLevel) - (trgMaxX >> sourceDataLevel) + 1;
int trgZSizeInSrc = (trgZ >> sourceDataLevel) - (trgMaxZ >> sourceDataLevel) + 1;
int trgXInSrc = (trgX >> sourceDataLevel) % srcPos.getWidth(sourceDataLevel).value;
int trgZInSrc = (trgZ >> sourceDataLevel) % srcPos.getWidth(sourceDataLevel).value;
ColumnQuadView srcView = dataSource.getDataInQuad(trgXInSrc, trgZInSrc, trgXSizeInSrc, trgZSizeInSrc);
ColumnQuadView trgView = renderDataSource.getFullQuad();
trgView.mergeMultiColumnFrom(srcView);
} else {
if (!(dataSources.get(0) instanceof FullDatatype))
throw new IllegalArgumentException("Unsupported data source type: " + dataSources.get(0).getClass().getName());
FullDatatype dataSource = (FullDatatype) dataSources.get(0);
DhSectionPos srcPos = dataSource.getSectionPos();
//TODO: Impl this
LodUtil.assertTrue(false,"Not implemented yet");
}
} else {
// If there are multiple data sources, we need to merge them into the target data source
for (LodDataSource dataSource : dataSources) {
byte targetDataLevel = (byte) (sectionPos.sectionDetail-SECTION_SIZE_OFFSET);
byte sourceDataLevel = dataSource.getDataDetail();
DhSectionPos srcPos = dataSource.getSectionPos();
if (dataSource instanceof ColumnDatatype) {
ColumnDatatype clDataSource = (ColumnDatatype) dataSource;
// Note that targetDataLevel can be > source section level
int srcX = srcPos.getCorner().getX().toBlock();
int srcZ = srcPos.getCorner().getZ().toBlock();
int srcMaxX = srcX + srcPos.getWidth().toBlock() - 1;
int srcMaxZ = srcZ + srcPos.getWidth().toBlock() - 1;
int srcXSizeInTrg = (srcX >> targetDataLevel) - (srcMaxX >> targetDataLevel) + 1;
int srcZSizeInTrg = (srcZ >> targetDataLevel) - (srcMaxZ >> targetDataLevel) + 1;
int srcXInTrg = (srcX >> targetDataLevel) % SECTION_SIZE;
int srcZInTrg = (srcZ >> targetDataLevel) % SECTION_SIZE;
ColumnQuadView srcView = clDataSource.getFullQuad();
ColumnQuadView trgView = renderDataSource.getDataInQuad(srcXInTrg, srcZInTrg, srcXSizeInTrg, srcZSizeInTrg);
trgView.mergeMultiColumnFrom(srcView);
} else {
if (!(dataSource instanceof FullDatatype))
throw new IllegalArgumentException("Unsupported data source type: " + dataSource.getClass().getName());
FullDatatype flDataSource = (FullDatatype) dataSource;
//TODO: Impl this
LodUtil.assertTrue(false,"Not implemented yet");
}
}
}
return renderDataSource;
}
@Override
public List<DataFile> selectFiles(DhSectionPos sectionPos, DHLevel level, List<DataFile>[] availableFiles) {
byte targetDataLevel = (byte) (sectionPos.sectionDetail - SECTION_SIZE_OFFSET);
//No support for loading higher than the target level yet.
byte maxDataLevel = LodUtil.min((byte) (availableFiles.length-1), targetDataLevel);
byte topValidDataLevel = Byte.MIN_VALUE;
List<DataFile> selectedFiles = new LinkedList<>();
// Select the one that is from lower level
}
for (int detail = maxDataLevel; detail >= 0; detail--) {
if (availableFiles[detail] == null) continue;
if (topValidDataLevel == Byte.MIN_VALUE) {
for (DataFile dataFile : availableFiles[detail]) {
if (dataFile.dataLevel > targetDataLevel) continue;
if (dataFile.dataType == ColumnDatatype.class || dataFile.dataType == FullDatatype.class) {
topValidDataLevel = LodUtil.max(topValidDataLevel, dataFile.dataLevel);
break;
}
}
}
if (topValidDataLevel == Byte.MIN_VALUE) continue;
DataFile singleCoveringColumnFile = null;
DataFile singleCoveringFullFile = null;
for (DataFile dataFile : availableFiles[detail]) {
if (dataFile.pos.getWidth().toBlock() == sectionPos.getWidth().toBlock()) {
if (dataFile.dataType == ColumnDatatype.class) {
singleCoveringColumnFile = dataFile;
break;
}
else if (dataFile.dataType == FullDatatype.class) {
singleCoveringFullFile = dataFile;
// Don't break as there may be a column file later.
}
} else if (dataFile.pos.getWidth().toBlock() > sectionPos.getWidth().toBlock()) {
if (dataFile.dataType == ColumnDatatype.class && singleCoveringColumnFile == null)
singleCoveringColumnFile = dataFile;
else if (dataFile.dataType == FullDatatype.class && singleCoveringFullFile == null)
singleCoveringFullFile = dataFile;
}
}
// First, try select single file that has enough width to cover the section
if (singleCoveringColumnFile != null) return Collections.singletonList(singleCoveringColumnFile);
if (singleCoveringFullFile != null) return Collections.singletonList(singleCoveringFullFile);
// If no single file covers the section, try to select all files without any duplicates
for (DataFile dataFile : availableFiles[detail]) {
boolean isDuplicate = false;
boolean isSet = false;
for (int i = 0; i < selectedFiles.size(); i++) {
DataFile selectedFile = selectedFiles.get(i);
if (selectedFile == null) continue;
if (selectedFile.pos.overlaps(dataFile.pos)) {
// Now, the already selected file muct have same or higher data level
// so, we just select the file with a position that covers the most area.
// Therefore, we choose the file with the higher section level.
if (selectedFile.pos.sectionDetail < dataFile.pos.sectionDetail) {
if (isSet) selectedFiles.set(i, null);
else selectedFiles.set(i, dataFile);
isSet = true;
} else {
LodUtil.assertTrue(!isSet); // We should not have encountered a smaller section level.
// This mean its completely covered by the selected file, so we can skip it.
isDuplicate = true;
break;
}
}
}
if (!isDuplicate && !isSet) selectedFiles.add(dataFile);
}
}
if (topValidDataLevel == Byte.MIN_VALUE) return Collections.emptyList();
selectedFiles.removeIf(Objects::isNull);
return selectedFiles;
}
};
static {
LodQuadTree.registerLayerLoader(COLUMN_LAYER_LOADER, (byte) 7); // 7 or above
}
public static RenderDataSource loadByCasting(LodDataSource dataSource, DhSectionPos sectionPos) {
if (dataSource instanceof ColumnDatatype) {
return (RenderDataSource) dataSource;
}
return null;
}
// public static RenderDataSource loadByCopying(LodDataSource dataSource, DhSectionPos sectionPos) {
//
// ColumnDatatype columns = new ColumnDatatype(sectionPos, dataSource,
// DetailDistanceUtil.getMaxVerticalData(dataDetail));
// //TODO
//
// return null;
// }
// static {
// RenderDataSource.registorLoader(ColumnDatatype::loadByCasting, 100);
// }
@Override
public DataSourceLoader getLatestLoader() {
return (DhSectionPos sectionPos, InputStream is) -> load(sectionPos, is, LATEST_VERSION);
return (DHLevel level, DhSectionPos sectionPos, InputStream data) -> loadFile(level, sectionPos, data, LATEST_VERSION);
}
@Override
@@ -0,0 +1,127 @@
package com.seibel.lod.core.objects.a7.datatype.column;
import java.util.Iterator;
public class ColumnQuadView implements ColumnDataView {
private final long[] data;
private final int perRowOffset; // per row offset in longs
private final int xSize; // x size in datapoints
private final int zSize; // x size in datapoints
private final int offset; // offset in longs
private final int vertSize; // vertical size in longs
public ColumnQuadView(long[] data, int dataZWidth, int dataVertSize, int viewXOffset, int viewZOffset, int xSize, int zSize) {
if (viewXOffset + xSize > (data.length / (dataZWidth* dataVertSize)) || viewZOffset + zSize > dataZWidth)
throw new IllegalArgumentException("View is out of bounds");
this.data = data;
this.xSize = xSize;
this.zSize = zSize;
this.vertSize = dataVertSize;
this.perRowOffset = dataZWidth * dataVertSize;
this.offset = (viewXOffset * perRowOffset + viewZOffset) * dataVertSize;
}
private ColumnQuadView(long[] data, int perRowOffset, int offset, int vertSize, int xSize, int zSize) {
this.data = data;
this.perRowOffset = perRowOffset;
this.offset = offset;
this.vertSize = vertSize;
this.xSize = xSize;
this.zSize = zSize;
}
@Override
public long get(int index) {
int x = index % (xSize * vertSize);
int z = index / (xSize * vertSize);
int v = index % vertSize;
return get(x, z, v);
}
public long get(int x, int z, int v) {
return data[offset + x * perRowOffset + z * vertSize + v];
}
public long set(int x, int z, int v, long value) {
return data[offset + x * perRowOffset + z * vertSize + v] = value;
}
public ColumnArrayView get(int x, int z) {
return new ColumnArrayView(data, vertSize, offset + x * perRowOffset + z * vertSize, vertSize);
}
public ColumnArrayView getRow(int x) {
return new ColumnArrayView(data, vertSize, offset + x * perRowOffset, zSize * vertSize);
}
public void set(int x, int z, ColumnDataView singleColumn) {
if (singleColumn.verticalSize() != vertSize) throw new IllegalArgumentException("Vertical size of singleColumn must be equal to vertSize");
if (singleColumn.dataCount() != 1) throw new IllegalArgumentException("SingleColumn must contain exactly one data point");
singleColumn.copyTo(data, x * perRowOffset + z * vertSize);
}
@Override
public int size() {
return xSize * zSize * vertSize;
}
@Override
public int verticalSize() {
return vertSize;
}
@Override
public int dataCount() {
return xSize * zSize;
}
@Override
public ColumnDataView subView(int dataIndexStart, int dataCount) {
if (dataCount != 1) throw new UnsupportedOperationException("Fixme: subView for QUadView only support one data point!");
int x = dataIndexStart % xSize;
int z = dataIndexStart / xSize;
return new ColumnArrayView(data, vertSize, offset + x * perRowOffset + z * vertSize, vertSize);
}
public ColumnQuadView subView(int xOffset, int zOffset, int xSize, int zSize) {
if (xOffset + xSize > this.xSize || zOffset + zSize > this.zSize) throw new IllegalArgumentException("SubView is out of bounds");
return new ColumnQuadView(data, perRowOffset, offset + xOffset * perRowOffset + zOffset * vertSize, vertSize, xSize, zSize);
}
@Override
public void copyTo(long[] target, int offset) {
for (int x = 0; x < xSize; x++) {
System.arraycopy(data, this.offset + x * perRowOffset, target, offset + x * xSize * vertSize, zSize * vertSize);
}
}
public void copyTo(ColumnQuadView target) {
if (target.xSize != xSize || target.zSize != zSize)
throw new IllegalArgumentException("Target view must have same size as this view");
for (int x = 0; x < xSize; x++) {
target.getRow(x).changeVerticalSizeFrom(getRow(x));
}
}
public void mergeMultiColumnFrom(ColumnQuadView source) {
if (source.xSize == xSize && source.zSize == zSize)
{
source.copyTo(this);
return;
}
if (source.xSize < xSize || source.zSize < zSize)
throw new IllegalArgumentException("Source view must have same or larger size as this view");
int srcXPerTrgX = source.xSize / xSize;
int srcZPerTrgZ = source.zSize / zSize;
if (source.xSize % xSize != 0 || source.zSize % zSize != 0)
throw new IllegalArgumentException("Source view's size must be a multiple of this view's size");
for (int x = 0; x < xSize; x++) {
for (int z = 0; z < zSize; z++) {
ColumnQuadView srcBlock = source.subView(x * srcXPerTrgX, z * srcZPerTrgZ, srcXPerTrgX, srcZPerTrgZ);
get(x, z).mergeMultiDataFrom(srcBlock);
}
}
}
}
@@ -1,4 +1,26 @@
package com.seibel.lod.core.objects.a7.datatype.full;
public class FullDatatype {
import com.seibel.lod.core.objects.a7.data.LodDataSource;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
public class FullDatatype implements LodDataSource {
@Override
public DataSourceLoader getLatestLoader() {
return null;
}
@Override
public <T> T[] getData() {
return null;
}
@Override
public DhSectionPos getSectionPos() {
return null;
}
@Override
public byte getDataDetail() {
return 0;
}
}
@@ -69,4 +69,9 @@ public class DhLodPos {
return this.equals(other.convertUpwardsTo(this.detail));
}
}
public DhLodPos add(DhLodUnit width) {
if (width.detail < detail) throw new IllegalArgumentException("add called with width.detail < pos detail");
return new DhLodPos(detail, x + width.convertTo(detail).value, z + width.convertTo(detail).value);
}
}
@@ -22,8 +22,8 @@ public class DhLodUnit {
return this;
}
if (detail > targetDetail) { //TODO check if this is correct
return new DhLodUnit(targetDetail, value >> (detail - targetDetail));
return new DhLodUnit(targetDetail, value << (detail - targetDetail));
}
return new DhLodUnit(targetDetail, value << (targetDetail - detail));
return new DhLodUnit(targetDetail, value >> (targetDetail - detail));
}
}
@@ -1,9 +1,5 @@
package com.seibel.lod.core.objects.a7.render;
import com.seibel.lod.core.objects.a7.DHLevel;
import com.seibel.lod.core.objects.a7.data.LodDataSource;
import com.seibel.lod.core.objects.a7.data.DataFile;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import com.seibel.lod.core.objects.opengl.RenderBuffer;
import java.util.*;
@@ -47,15 +43,6 @@ public interface RenderDataSource {
// return null;
// }
abstract class RenderDataSourceLoader {
public abstract RenderDataSource construct(LodDataSource[] dataSources, DhSectionPos sectionPos, DHLevel level);
public Set<DataFile> selectFiles(DhSectionPos sectionPos, DHLevel level, Set<DataFile> availableFiles) {
return Collections.singleton(availableFiles.iterator().next());
}
}
void enableRender();
void disableRender();
boolean isRenderReady();
@@ -0,0 +1,23 @@
package com.seibel.lod.core.objects.a7.render;
import com.seibel.lod.core.objects.a7.DHLevel;
import com.seibel.lod.core.objects.a7.data.DataFile;
import com.seibel.lod.core.objects.a7.data.LodDataSource;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import java.util.*;
import java.util.stream.Collectors;
public abstract class RenderDataSourceLoader {
public final int detailOffset;
public RenderDataSourceLoader(int detailOffset) {
this.detailOffset = detailOffset;
}
public abstract RenderDataSource construct(List<LodDataSource> dataSources, DhSectionPos sectionPos, DHLevel level);
public List<DataFile> selectFiles(DhSectionPos sectionPos, DHLevel level, List<DataFile>[] availableFiles) {
return Arrays.stream(availableFiles).flatMap(Collection::stream).collect(Collectors.toList());
}
}