Skip to content

Commit

Permalink
Simplify heightmap calculation and avoid loading empty sections
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell committed Dec 24, 2024
1 parent c8f1984 commit 5229769
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,13 @@ public boolean hasSection(int layer) {
return getSections(false)[layer] != null;
}

@Override
public boolean hasNonEmptySection(int layer) {
layer -= getMinSectionPosition();
LevelChunkSection section = getSections(false)[layer];
return section != null && !section.hasOnlyAir();
}

@Override
@SuppressWarnings("unchecked")
public synchronized boolean trim(boolean aggressive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,13 @@ public boolean hasSection(int layer) {
return getSections(false)[layer] != null;
}

@Override
public boolean hasNonEmptySection(int layer) {
layer -= getMinSectionPosition();
LevelChunkSection section = getSections(false)[layer];
return section != null && !section.hasOnlyAir();
}

@Override
@SuppressWarnings("unchecked")
public synchronized boolean trim(boolean aggressive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,13 @@ public boolean hasSection(int layer) {
return getSections(false)[layer] != null;
}

@Override
public boolean hasNonEmptySection(int layer) {
layer -= getMinSectionPosition();
LevelChunkSection section = getSections(false)[layer];
return section != null && !section.hasOnlyAir();
}

@Override
@SuppressWarnings("unchecked")
public synchronized boolean trim(boolean aggressive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,13 @@ public boolean hasSection(int layer) {
return getSections(false)[layer] != null;
}

@Override
public boolean hasNonEmptySection(int layer) {
layer -= getMinSectionPosition();
LevelChunkSection section = getSections(false)[layer];
return section != null && !section.hasOnlyAir();
}

@Override
@SuppressWarnings("unchecked")
public synchronized boolean trim(boolean aggressive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,13 @@ public boolean hasSection(int layer) {
return getSections(false)[layer] != null;
}

@Override
public boolean hasNonEmptySection(int layer) {
layer -= getMinSectionPosition();
LevelChunkSection section = getSections(false)[layer];
return section != null && !section.hasOnlyAir();
}

@Override
@SuppressWarnings("unchecked")
public synchronized boolean trim(boolean aggressive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,13 @@ public boolean hasSection(int layer) {
return getSections(false)[layer] != null;
}

@Override
public boolean hasNonEmptySection(int layer) {
layer -= getMinSectionPosition();
LevelChunkSection section = getSections(false)[layer];
return section != null && !section.hasOnlyAir();
}

@Override
@SuppressWarnings("unchecked")
public synchronized boolean trim(boolean aggressive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ public class HeightmapProcessor implements IBatchProcessor {
private static final HeightMapType[] TYPES = HeightMapType.values();
private static final int BLOCKS_PER_Y_SHIFT = 8; // log2(256)
private static final int BLOCKS_PER_Y = 256; // 16 x 16
private static final boolean[] COMPLETE = new boolean[BLOCKS_PER_Y];
private static final char[] AIR_LAYER = new char[4096];
private static final int NEEDED_UPDATES = TYPES.length * BLOCKS_PER_Y;

static {
Arrays.fill(COMPLETE, true);
Arrays.fill(AIR_LAYER, (char) BlockTypesCache.ReservedIDs.AIR);
}

Expand All @@ -49,13 +48,12 @@ private static int index(int y, int offset) {
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
// each heightmap gets one 16*16 array
int[][] heightmaps = new int[TYPES.length][BLOCKS_PER_Y];
boolean[][] updated = new boolean[TYPES.length][BLOCKS_PER_Y];
int skip = 0;
int allSkipped = (1 << TYPES.length) - 1; // lowest types.length bits are set
byte[] updated = new byte[BLOCKS_PER_Y];
int updateCount = 0; // count updates, this way we know when we're finished
layer:
for (int layer = maxY >> 4; layer >= minY >> 4; layer--) {
boolean hasSectionSet = set.hasSection(layer);
boolean hasSectionGet = get.hasSection(layer);
boolean hasSectionSet = set.hasNonEmptySection(layer);
boolean hasSectionGet = get.hasNonEmptySection(layer);
if (!(hasSectionSet || hasSectionGet)) {
continue;
}
Expand Down Expand Up @@ -103,30 +101,26 @@ public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
if (block == null) {
continue;
}
byte updateStateAtJ = updated[j];
for (int i = 0; i < TYPES.length; i++) {
if ((skip & (1 << i)) != 0) {
continue; // skip finished height map
int bitFlag = 1 << i;
if ((updateStateAtJ & bitFlag) != 0) {
continue; // skip finished height map at this column
}
HeightMapType type = TYPES[i];
// ignore if that position was already set
if (!updated[i][j] && type.includes(block)) {
if (type.includes(block)) {
// mc requires + 1, heightmaps are normalized internally, thus we need to "zero" them.
heightmaps[i][j] = ((layer - get.getMinSectionPosition()) << 4) + y + 1;
updated[i][j] = true; // mark as updated
updated[j] |= (byte) bitFlag; // mark as updated
if (++updateCount == NEEDED_UPDATES) {
break layer; // all heightmaps in all columns updated
}

}
}
}
}
for (int i = 0; i < updated.length; i++) {
if ((skip & (1 << i)) == 0 // if already true, skip array equality check
&& Arrays.equals(updated[i], COMPLETE)) {
skip |= 1 << i;
}
}
if (skip != allSkipped) {
continue;
}
break; // all maps are processed
}
for (int i = 0; i < TYPES.length; i++) {
set.setHeightMap(TYPES[i], heightmaps[i]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ public interface IBlocks extends Trimable {
*/
boolean hasSection(int layer);

/**
* {@return whether the chunk has a section that has any non-air/reserved blocks}
* This method might be conservative and return {@code true} even if the section is empty.
*
* @param layer the section's layer
* @since TODO
*/
default boolean hasNonEmptySection(int layer) {
return hasSection(layer);
}

/**
* Obtain the specified chunk section stored as an array of ordinals. Uses normal minecraft chunk-section position indices
* (length 4096). Operations synchronises on the section and will load the section into memory if not present. For chunk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,11 @@ public boolean hasSection(int layer) {
return chunkExisting != null && chunkExisting.hasSection(layer);
}

@Override
public boolean hasNonEmptySection(final int layer) {
return chunkExisting != null && chunkExisting.hasNonEmptySection(layer);
}

@Override
public synchronized void filterBlocks(Filter filter, ChunkFilterBlock block, @Nullable Region region, boolean full) {
final IChunkGet get = getOrCreateGet();
Expand Down

0 comments on commit 5229769

Please sign in to comment.