Reworked lodBuilder. Make it faster.
This commit is contained in:
@@ -32,7 +32,6 @@ import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
|
||||
@@ -163,14 +162,12 @@ public class LodBuilder
|
||||
public boolean generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk, LodBuilderConfig config, boolean override)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
//config.distanceGenerationMode = DistanceGenerationMode.FULL;
|
||||
|
||||
//long executeTime = System.currentTimeMillis();
|
||||
if (chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
|
||||
int startX;
|
||||
int startZ;
|
||||
|
||||
|
||||
LodRegion region = lodDim.getRegion(chunk.getRegionPosX(), chunk.getRegionPosZ());
|
||||
if (region == null)
|
||||
return false;
|
||||
@@ -183,30 +180,27 @@ public class LodBuilder
|
||||
|
||||
// determine how many LODs to generate vertically
|
||||
//VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
|
||||
|
||||
// generate the LODs
|
||||
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte)0);
|
||||
long[] data = new long[maxVerticalData*16*16];
|
||||
for (int i = 0; i < 16*16; i++)
|
||||
{
|
||||
int subX = i/16;
|
||||
int subZ = i%16;
|
||||
writeVerticalData(data, i*maxVerticalData, maxVerticalData, chunk, config, subX, subZ);
|
||||
}
|
||||
if (!chunk.isLightCorrect()) return false;
|
||||
|
||||
region.isWriting++;
|
||||
try {
|
||||
LodRegion newRegion = lodDim.getRegionFromFile(region, (byte)0, region.getGenerationMode(), region.getVerticalQuality());
|
||||
assert(region==newRegion);
|
||||
|
||||
// generate the LODs
|
||||
int posX;
|
||||
int posZ;
|
||||
for (int i = 0; i < 16*16; i++)
|
||||
{
|
||||
startX = i/16;
|
||||
startZ = i%16;
|
||||
|
||||
long[] data;
|
||||
long[] dataToMergeVertical = createVerticalDataToMerge((byte)0, chunk, config, startX, startZ);
|
||||
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.WORLD_HEIGHT / 2 + 1, DetailDistanceUtil.getMaxVerticalData((byte)0));
|
||||
|
||||
if (data != null && data.length != 0)
|
||||
{
|
||||
posX = chunk.getChunkPosX() * 16 + startX;
|
||||
posZ = chunk.getChunkPosZ() * 16 + startZ;
|
||||
if (region.addVerticalData((byte)0, posX, posZ, data, override))
|
||||
region.updateArea((byte)0, posX, posZ);
|
||||
}
|
||||
if (region.getMinDetailLevel()!= 0) {
|
||||
LodRegion newRegion = lodDim.getRegionFromFile(region, (byte)0, region.getGenerationMode(), region.getVerticalQuality());
|
||||
assert(region==newRegion);
|
||||
}
|
||||
if (region.addChunkOfData((byte)0, chunk.getMinX(), chunk.getMinZ(), 16, 16, data, maxVerticalData, true)) {
|
||||
region.regenerateLodFromArea((byte)0, chunk.getMinX(), chunk.getMinZ(), 16, 16);
|
||||
lodDim.regenDimensionBuffers = true;
|
||||
}
|
||||
} finally {
|
||||
region.isWriting--;
|
||||
@@ -216,81 +210,62 @@ public class LodBuilder
|
||||
//executeTime = System.currentTimeMillis() - executeTime;
|
||||
//if (executeTime > 0) ClientApi.LOGGER.info("generateLodNodeFromChunk level: " + detailLevel + " time ms: " + executeTime);
|
||||
}
|
||||
|
||||
|
||||
/** creates a vertical DataPoint */
|
||||
private long[] createVerticalDataToMerge(byte detail, IChunkWrapper chunk, LodBuilderConfig config, int startX, int startZ)
|
||||
private void writeVerticalData(long[] data, int dataOffset, int maxVerticalData,
|
||||
IChunkWrapper chunk, LodBuilderConfig config, int chunkSubPosX, int chunkSubPosZ)
|
||||
{
|
||||
// equivalent to 2^detailLevel
|
||||
int size = 1 << detail;
|
||||
|
||||
int totalVerticalData = (chunk.getHeight());
|
||||
long[] dataToMerge = new long[totalVerticalData];
|
||||
|
||||
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail);
|
||||
int verticalData = DataPointUtil.WORLD_HEIGHT / 2 + 1;
|
||||
int height;
|
||||
int depth;
|
||||
int color;
|
||||
int light;
|
||||
int lightSky;
|
||||
int lightBlock;
|
||||
int generation = config.distanceGenerationMode.complexity;
|
||||
|
||||
int xRel;
|
||||
int zRel;
|
||||
int xAbs;
|
||||
int yAbs;
|
||||
int zAbs;
|
||||
boolean hasCeiling = MC.getWrappedClientWorld().getDimensionType().hasCeiling();
|
||||
boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight();
|
||||
boolean isDefault;
|
||||
int index;
|
||||
int generation = config.distanceGenerationMode.complexity;
|
||||
int count = 0;
|
||||
// FIXME: This yAbs is just messy!
|
||||
int x = chunk.getMinX() + chunkSubPosX;
|
||||
int z = chunk.getMinZ() + chunkSubPosZ;
|
||||
int y = chunk.getMaxY(x, z);
|
||||
|
||||
for (index = 0; index < size * size; index++)
|
||||
{
|
||||
xRel = startX + index % size;
|
||||
zRel = startZ + index / size;
|
||||
xAbs = chunk.getMinX() + xRel;
|
||||
zAbs = chunk.getMinZ() + zRel;
|
||||
|
||||
//Calculate the height of the lod
|
||||
yAbs = chunk.getMaxY(xRel,zRel) - MIN_WORLD_HEIGHT;
|
||||
int count = 0;
|
||||
boolean topBlock = true;
|
||||
if (yAbs <= 0)
|
||||
dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
|
||||
while (yAbs > 0)
|
||||
{
|
||||
height = determineHeightPointFrom(chunk, config, xAbs, yAbs, zAbs);
|
||||
|
||||
// If the lod is at the default height, it must be void data
|
||||
if (height == 0)
|
||||
break;
|
||||
|
||||
yAbs = height - 1;
|
||||
// We search light on above air block
|
||||
depth = determineBottomPointFrom(chunk, config, xAbs, yAbs, zAbs, count < this.config.client().graphics().quality().getVerticalQuality().maxConnectedLods && !hasCeiling);
|
||||
if (hasCeiling && topBlock)
|
||||
yAbs = depth;
|
||||
light = getLightValue(chunk, xAbs,yAbs + MIN_WORLD_HEIGHT, zAbs, hasCeiling, hasSkyLight, topBlock);
|
||||
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs);
|
||||
lightBlock = light & 0b1111;
|
||||
lightSky = (light >> 4) & 0b1111;
|
||||
isDefault = ((light >> 8)) == 1;
|
||||
|
||||
dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height, depth, color, lightSky, lightBlock, generation, isDefault);
|
||||
topBlock = false;
|
||||
yAbs = depth - 1;
|
||||
count++;
|
||||
}
|
||||
boolean topBlock = true;
|
||||
if (y <= chunk.getMinBuildHeight())
|
||||
data[dataOffset] = DataPointUtil.createVoidDataPoint(generation);
|
||||
while (y > chunk.getMinBuildHeight()) {
|
||||
int height = determineHeightPointFrom(chunk, config, x, y, z);
|
||||
// If the lod is at the default height, it must be void data
|
||||
if (height <= chunk.getMinBuildHeight())
|
||||
break;
|
||||
y = height - 1;
|
||||
// We search light on above air block
|
||||
int depth = determineBottomPointFrom(chunk, config, x, y, z,
|
||||
count < this.config.client().graphics().quality().getVerticalQuality().maxConnectedLods
|
||||
&& !hasCeiling);
|
||||
if (hasCeiling && topBlock)
|
||||
y = depth;
|
||||
int light = getLightValue(chunk, x, y, z, hasCeiling, hasSkyLight, topBlock);
|
||||
int color = generateLodColor(chunk, config, x, y, z);
|
||||
int lightBlock = light & 0b1111;
|
||||
int lightSky = (light >> 4) & 0b1111;
|
||||
boolean isDefault = ((light >> 8)) == 1;
|
||||
dataToMerge[count] = DataPointUtil.createDataPoint(height-chunk.getMinBuildHeight(), depth-chunk.getMinBuildHeight(),
|
||||
color, lightSky, lightBlock, generation, isDefault);
|
||||
topBlock = false;
|
||||
y = depth - 1;
|
||||
count++;
|
||||
}
|
||||
return dataToMerge;
|
||||
long[] result = DataPointUtil.mergeMultiData(dataToMerge, totalVerticalData, maxVerticalData);
|
||||
if (result.length != maxVerticalData) throw new ArrayIndexOutOfBoundsException();
|
||||
System.arraycopy(result, 0, data, dataOffset, maxVerticalData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
* Used when creating a vertical LOD.
|
||||
*/
|
||||
private short determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, boolean strictEdge)
|
||||
private int determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, boolean strictEdge)
|
||||
{
|
||||
short depth = 0;
|
||||
int depth = chunk.getMinBuildHeight();
|
||||
|
||||
int colorOfBlock = 0;
|
||||
if (strictEdge)
|
||||
@@ -308,7 +283,7 @@ public class LodBuilder
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = yAbs - 1; y >= 0; y--)
|
||||
for (int y = yAbs - 1; y >= chunk.getMinBuildHeight(); y--)
|
||||
{
|
||||
|
||||
if (!isLayerValidLodPoint(chunk, xAbs, y, zAbs)
|
||||
@@ -322,21 +297,16 @@ public class LodBuilder
|
||||
}
|
||||
|
||||
/** Find the highest valid point from the Top */
|
||||
private short determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs)
|
||||
private int determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs)
|
||||
{
|
||||
//TODO find a way to skip bottom of the world
|
||||
short height = 0;
|
||||
if (config.useHeightmap)
|
||||
height = (short) chunk.getHeightMapValue(xAbs, zAbs);
|
||||
else
|
||||
int height = chunk.getMinBuildHeight();
|
||||
for (int y = yAbs; y >= chunk.getMinBuildHeight(); y--)
|
||||
{
|
||||
for (int y = yAbs; y >= 0; y--)
|
||||
if (isLayerValidLodPoint(chunk, xAbs, y, zAbs))
|
||||
{
|
||||
if (isLayerValidLodPoint(chunk, xAbs, y, zAbs))
|
||||
{
|
||||
height = (short) (y + 1);
|
||||
break;
|
||||
}
|
||||
height = (y + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return height;
|
||||
|
||||
@@ -42,10 +42,16 @@ public interface LevelContainer
|
||||
* @param data actual data to add in an array of long[] format.
|
||||
* @param posX x position in the detail level
|
||||
* @param posZ z position in the detail level
|
||||
* @return true if correctly added, false otherwise
|
||||
* @return true if correctly changed, false otherwise
|
||||
*/
|
||||
boolean addVerticalData(long[] data, int posX, int posZ, boolean override);
|
||||
|
||||
/**
|
||||
* With this you can add a square of data to the level container
|
||||
* @return true if anything changed, false otherwise
|
||||
*/
|
||||
boolean addChunkOfData(long[] data, int posX, int posZ, int widthX, int widthZ, boolean override);
|
||||
|
||||
/**
|
||||
* With this you can add data to the level container
|
||||
* @param data actual data to add in an array of long format.
|
||||
@@ -115,4 +121,5 @@ public interface LevelContainer
|
||||
* @return data as a String
|
||||
*/
|
||||
int getMaxNumberOfLods();
|
||||
|
||||
}
|
||||
|
||||
@@ -90,7 +90,6 @@ public class LodRegion {
|
||||
/**
|
||||
* Inserts the data point into the region.
|
||||
* <p>
|
||||
* TODO this will always return true unless it has
|
||||
*
|
||||
* @return true if the data was added successfully
|
||||
*/
|
||||
@@ -112,7 +111,6 @@ public class LodRegion {
|
||||
/**
|
||||
* Inserts the vertical data into the region.
|
||||
* <p>
|
||||
* TODO this will always return true unless it has
|
||||
*
|
||||
* @return true if the data was added successfully
|
||||
*/
|
||||
@@ -133,6 +131,31 @@ public class LodRegion {
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the vertical data into the region.
|
||||
* <p>
|
||||
*
|
||||
* @return true if the data was added successfully
|
||||
*/
|
||||
public boolean addChunkOfData(byte detailLevel, int posX, int posZ, int widthX, int widthZ, long[] data, int verticalSize, boolean override) {
|
||||
assert(isWriting!=0);
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
|
||||
// The dataContainer could have null entries if the
|
||||
// detailLevel changes.
|
||||
if (this.dataContainer[detailLevel] == null)
|
||||
return false;// this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
|
||||
if (this.dataContainer[detailLevel].getVerticalSize() != verticalSize) throw new RuntimeException();
|
||||
|
||||
boolean updated = this.dataContainer[detailLevel].addChunkOfData(data, posX, posZ, widthX, widthZ, override);
|
||||
if (updated) {
|
||||
needRegenBuffer = 2;
|
||||
needSaving = true;
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dataPoint at the given relative position.
|
||||
@@ -377,17 +400,53 @@ public class LodRegion {
|
||||
update(up, LevelPosUtil.convert(detailLevel, posX, up), LevelPosUtil.convert(detailLevel, posZ, up));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean regenerateLodFromArea(byte detailLevel, int posX, int posZ, int widthX, int widthZ) {
|
||||
if (detailLevel >= LodUtil.REGION_DETAIL_LEVEL) return false;
|
||||
|
||||
if (detailLevel < minDetailLevel) {
|
||||
byte startLevel = minDetailLevel;
|
||||
int maxPosX = Math.floorDiv(posX+widthX-1, (1 << (minDetailLevel-startLevel)))+1;
|
||||
int maxPosZ = Math.floorDiv(posZ+widthZ-1, (1 << (minDetailLevel-startLevel)))+1;
|
||||
posX = Math.floorDiv(posX, (1 << (minDetailLevel-startLevel)));
|
||||
posZ = Math.floorDiv(posZ, (1 << (minDetailLevel-startLevel)));
|
||||
widthX = maxPosX-posX;
|
||||
widthZ = maxPosZ-posZ;
|
||||
detailLevel = minDetailLevel;
|
||||
}
|
||||
do {
|
||||
int maxPosX = Math.floorDiv(posX+widthX-1, 2)+1;
|
||||
int maxPosZ = Math.floorDiv(posZ+widthZ-1, 2)+1;
|
||||
posX = Math.floorDiv(posX, 2);
|
||||
posZ = Math.floorDiv(posZ, 2);
|
||||
widthX = maxPosX-posX;
|
||||
widthZ = maxPosZ-posZ;
|
||||
detailLevel++;
|
||||
chunkUpdate(detailLevel, posX, posZ, widthX, widthZ);
|
||||
} while (detailLevel < LodUtil.REGION_DETAIL_LEVEL);
|
||||
|
||||
needRegenBuffer = 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the child at the given relative Pos
|
||||
* <p>
|
||||
* TODO could this be renamed mergeChildData?
|
||||
* TODO make this return whether any value has changed
|
||||
*/
|
||||
private void update(byte detailLevel, int posX, int posZ) {
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ);
|
||||
}
|
||||
private void chunkUpdate(byte detailLevel, int posX, int posZ, int widthX,int widthZ) {
|
||||
for (int ox=0; ox<widthX; ox++) {
|
||||
for (int oz=0; oz<widthZ; oz++) {
|
||||
update(detailLevel, posX+ox, posZ+oz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if data exists at the given relative Pos.
|
||||
|
||||
@@ -103,6 +103,24 @@ public class VerticalLevelContainer implements LevelContainer
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addChunkOfData(long[] data, int posX, int posZ, int widthX, int widthZ, boolean override)
|
||||
{
|
||||
boolean anyChange = false;
|
||||
if (posX+widthX > size || posZ+widthZ > size)
|
||||
throw new IndexOutOfBoundsException("addChunkOfData param not inside valid range");
|
||||
if (widthX*widthZ*verticalSize > data.length)
|
||||
throw new IndexOutOfBoundsException("addChunkOfData data array not long enough to contain the data to be copied");
|
||||
|
||||
for (int ox=0; ox<widthX; ox++) {
|
||||
anyChange = DataPointUtil.mergeTwoDataArray(
|
||||
dataContainer, ((ox+posX)*size+posZ) * verticalSize,
|
||||
data, ox*widthX*verticalSize,
|
||||
widthZ, verticalSize, override);
|
||||
}
|
||||
return anyChange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addSingleData(long data, int posX, int posZ)
|
||||
{
|
||||
@@ -1023,7 +1041,6 @@ public class VerticalLevelContainer implements LevelContainer
|
||||
posZ);
|
||||
}
|
||||
|
||||
|
||||
private void mergeAndAddColorLightData(int sliceStart, int sliceEnd, int posZ, int posX, short[] inputPositionData, int[] inputVerticalData, int[] inputColorData, byte[] inputLightData, byte inputDetailLevel, int inputVerticalSize)
|
||||
{
|
||||
//STEP 5//
|
||||
|
||||
@@ -269,6 +269,45 @@ public class DataPointUtil
|
||||
return genA-genB;
|
||||
}
|
||||
|
||||
// Merge the newData data into the target data by comparing the Datapoint Priority.
|
||||
// If target and newData are same priority, it will use the target one.
|
||||
// If target and newData size are different, it will copy up to the smallest one.
|
||||
// Return whether anything changed
|
||||
public static boolean mergeTwoDataArray(long[] target, long[] newData, int verticalDataSize) {
|
||||
boolean anyChange = false;
|
||||
for (int i=0; i<target.length&&i<newData.length; i+=verticalDataSize) {
|
||||
if (compareDatapointPriority(newData[i], target[i])>0) {
|
||||
anyChange = true;
|
||||
System.arraycopy(newData, i, target, i, verticalDataSize);
|
||||
}
|
||||
}
|
||||
return anyChange;
|
||||
}
|
||||
|
||||
// Same as above, but with args for buffer offset.
|
||||
// Return whether anything changed
|
||||
public static boolean mergeTwoDataArray(long[] target, int targetOffset, long[] newData, int newDataOffset, int dataLength, int verticalDataSize, boolean override) {
|
||||
if (targetOffset + verticalDataSize*dataLength>target.length)
|
||||
throw new ArrayIndexOutOfBoundsException("\"target\" array index out of bounds");
|
||||
if (newDataOffset + verticalDataSize*dataLength>newData.length)
|
||||
throw new ArrayIndexOutOfBoundsException("\"newData\" array index out of bounds");
|
||||
boolean anyChange = false;
|
||||
for (int o=0; o<(dataLength*verticalDataSize); o+=verticalDataSize) {
|
||||
if (override) {
|
||||
if (compareDatapointPriority(newData[o+newDataOffset], target[o+targetOffset])>=0) {
|
||||
anyChange = true;
|
||||
System.arraycopy(newData, o+newDataOffset, target, o+targetOffset, verticalDataSize);
|
||||
}
|
||||
} else {
|
||||
if (compareDatapointPriority(newData[o+newDataOffset], target[o+targetOffset])>0) {
|
||||
anyChange = true;
|
||||
System.arraycopy(newData, o+newDataOffset, target, o+targetOffset, verticalDataSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
return anyChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method merge column of multiple data together
|
||||
* @param dataToMerge one or more columns of data
|
||||
|
||||
@@ -29,9 +29,11 @@ import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
*/
|
||||
public interface IChunkWrapper
|
||||
{
|
||||
int getHeight();
|
||||
|
||||
boolean isPositionInWater(int x, int y, int z);
|
||||
default int getHeight() {
|
||||
return getMaxBuildHeight()-getMinBuildHeight();
|
||||
}
|
||||
int getMinBuildHeight();
|
||||
int getMaxBuildHeight();
|
||||
|
||||
int getHeightMapValue(int xRel, int zRel);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user