Finish up the ColumnDatatype and related copy and stuff
This commit is contained in:
@@ -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);
|
||||
}
|
||||
+211
-471
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user