diff --git a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java b/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java index 208075d64..4ae3405cf 100644 --- a/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java +++ b/src/main/java/com/seibel/lod/core/builders/lodBuilding/LodBuilder.java @@ -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; diff --git a/src/main/java/com/seibel/lod/core/objects/lod/LevelContainer.java b/src/main/java/com/seibel/lod/core/objects/lod/LevelContainer.java index f05f4001c..9f9fb0024 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/LevelContainer.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/LevelContainer.java @@ -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(); + } diff --git a/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java b/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java index 90ecbe205..ac2f7e15c 100644 --- a/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java +++ b/src/main/java/com/seibel/lod/core/objects/lod/LodRegion.java @@ -90,7 +90,6 @@ public class LodRegion { /** * Inserts the data point into the region. *
- * 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. *
- * 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. + *
+ * + * @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 *
* 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