diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/ChunkGeneratorResult.java b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/ChunkGeneratorResult.java index 5b68dbd53..7a7ab7400 100644 --- a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/ChunkGeneratorResult.java +++ b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/ChunkGeneratorResult.java @@ -54,6 +54,13 @@ public class ChunkGeneratorResult { this.sectionBlocksArray[section][ChunkSection.makeBlockIndex(x, cy, z)] = (short) id; } + public short @NotNull [] @Nullable [] getSectionBlocksArray() { + return this.sectionBlocksArray; + } + public int @NotNull [] getNumBlocksInSectionArray() { + return this.numBlocksInSectionArray; + } + public int getBlock(final int x, final int y, final int z) { if ( x < 0 || x >= Chunk.CHUNK_SIZE_X || diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/overworld/SurfaceGeneratorOverworld.java b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/overworld/SurfaceGeneratorOverworld.java index 9f6523e93..f4489bd12 100644 --- a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/overworld/SurfaceGeneratorOverworld.java +++ b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/overworld/SurfaceGeneratorOverworld.java @@ -8,6 +8,7 @@ import net.minecraft.core.world.biome.Biome; import net.minecraft.core.world.biome.BiomeTags; import net.minecraft.core.world.biome.Biomes; import net.minecraft.core.world.chunk.Chunk; +import net.minecraft.core.world.chunk.ChunkSection; import net.minecraft.core.world.generate.chunk.ChunkGeneratorResult; import net.minecraft.core.world.generate.chunk.perlin.SurfaceGenerator; import net.minecraft.core.world.noise.*; @@ -54,12 +55,12 @@ public class SurfaceGeneratorOverworld int oceanY = world.getWorldType().getOceanY(); int minY = world.getWorldType().getMinY(world); int maxY = world.getWorldType().getMaxY(world); - int terrainHeight = (maxY + 1) - minY; + // int terrainHeight = (maxY + 1) - minY; int chunkX = chunk.pos.x; int chunkZ = chunk.pos.z; - int oceanBlock = world.getWorldType().getOceanBlockIds()[0]; - int worldFillBlock = world.getWorldType().getFillerBlockId(); + final int oceanBlock = world.getWorldType().getOceanBlockIds()[0]; + final int worldFillBlock = world.getWorldType().getFillerBlockId(); Random rand = new Random((long) chunkX * 0x4F9939F508L + (long) chunkZ * 0x1EF1565BD5L); @@ -82,9 +83,11 @@ public class SurfaceGeneratorOverworld { for(int x = 0; x < Chunk.CHUNK_SIZE_X; x++) { - boolean generateSandBeach = sandBeachNoise[z + (x * Chunk.CHUNK_SIZE_Z)] + rand.nextDouble() * 0.2D > 0.0D; - boolean generateGravelBeach = gravelBeachNoise[z + (x * Chunk.CHUNK_SIZE_Z)] + rand.nextDouble() * 0.2D > 3D; - int soilThickness = (int)(soilThicknessNoise[z + (x * Chunk.CHUNK_SIZE_Z)] / 3D + 3D + rand.nextDouble() * 0.25D); + final int noiseIndex = z + (x * Chunk.CHUNK_SIZE_Z); + + final boolean generateSandBeach = sandBeachNoise[noiseIndex] + rand.nextDouble() * 0.2D > 0.0D; + final boolean generateGravelBeach = gravelBeachNoise[noiseIndex] + rand.nextDouble() * 0.2D > 3D; + final int soilThickness = (int)(soilThicknessNoise[noiseIndex] / 3D + 3D + rand.nextDouble() * 0.25D); boolean generateBasaltLayer = false; boolean generateGraniteLayer = false; @@ -96,9 +99,9 @@ public class SurfaceGeneratorOverworld if (generateStoneVariants) { - generateBasaltLayer = stoneLayerNoiseBasalt[z + (x * Chunk.CHUNK_SIZE_Z)] + rand.nextDouble() * 0.2D > 0D; - generateGraniteLayer = stoneLayerNoiseGranite[z + (x * Chunk.CHUNK_SIZE_Z)] + rand.nextDouble() * 0.2D > 2D; - generateLimestoneLayer = stoneLayerNoiseLimestone[z + (x * Chunk.CHUNK_SIZE_Z)] + rand.nextDouble() * 0.2D > 3D; + generateBasaltLayer = stoneLayerNoiseBasalt[noiseIndex] + rand.nextDouble() * 0.2D > 0D; + generateGraniteLayer = stoneLayerNoiseGranite[noiseIndex] + rand.nextDouble() * 0.2D > 2D; + generateLimestoneLayer = stoneLayerNoiseLimestone[noiseIndex] + rand.nextDouble() * 0.2D > 3D; basaltThicknessLevel = (int)(stoneLayerNoiseBasalt[z + x] + rand.nextDouble() * 0.5D); graniteThicknessLevel = (int)(stoneLayerNoiseGranite[z + x] + rand.nextDouble() * 0.5D); limestoneThicknessLevel = (int)(stoneLayerNoiseLimestone[z + x] + rand.nextDouble() * 0.5D); @@ -108,38 +111,58 @@ public class SurfaceGeneratorOverworld short topBlock = -1; short fillerBlock = -1; - Biome lastBiome = null; - - for(int y = maxY; y >= minY; y--) + top_down: for(int y = maxY; y >= minY; y--) { - Biome biome = chunk.getBlockBiome(x, y, z); - if (biome == null) biome = world.getBiomeProvider().getBiome(chunkX * Chunk.CHUNK_SIZE_X + x, y >> 3, chunkZ * Chunk.CHUNK_SIZE_Z + z); - - int block = result.getBlock(x, y, z); - - if ((biome != lastBiome || topBlock == -1 || fillerBlock == -1) && block == 0) - { - topBlock = (short) biome.getSurfaceProperties().getTopBlock().id(); - fillerBlock = (short) biome.getSurfaceProperties().getFillerBlock().id(); - } - lastBiome = biome; + // finds the first block that == worldFillBlock from the chunkGeneratorResult, + // and set currentLayerDepth to -1 if any air is encountered along the way + search_fillblock: { + final class ctx { + @SuppressWarnings("unused") static int sectionPrefetch; + } - // reset the currently generating surface thickness to -1 if air encountered - if(block == 0) - { - currentLayerDepth = -1; - continue; + int outerY = y / ChunkSection.SECTION_SIZE_Y, innerY = y % ChunkSection.SECTION_SIZE_Y; + int outerYMin = minY / ChunkSection.SECTION_SIZE_Y, innerYMin = minY % ChunkSection.SECTION_SIZE_Y; + + final int MAX_Y = ChunkSection.SECTION_SIZE_Y - 1; + final int CSIZE_XZ = Chunk.CHUNK_SIZE_Z * Chunk.CHUNK_SIZE_X; + + // final int i_ = ChunkSection.makeBlockIndex(x, MAX_Y, z); + final int topIndex = (MAX_Y * CSIZE_XZ) + (z * Chunk.CHUNK_SIZE_X) + x; + int index = topIndex - CSIZE_XZ * (MAX_Y - innerY); + + final var sections = result.getSectionBlocksArray(); + final var sectionBlocks = result.getNumBlocksInSectionArray(); + + for (; outerY >= outerYMin; outerY--, innerY = MAX_Y, index = topIndex) { + if (sectionBlocks[outerY] == 0) continue; + final var section = sections[outerY]; + if (section == null) continue; + + final int min = (outerY == outerYMin) ? innerYMin : 0; + + ctx.sectionPrefetch = section[0]; + for (; innerY >= min; innerY--, index -= CSIZE_XZ) { + final var block = section[index]; + // yippee + if (block == worldFillBlock) { + y = outerY * ChunkSection.SECTION_SIZE_Y + innerY; + break search_fillblock; + } + if (block == 0) currentLayerDepth = -1; + } + } + break top_down; } - // will skip a generation loop if it encounters something other than stone - if(block != worldFillBlock) - { - continue; - } + Biome biome = chunk.getBlockBiome(x, y, z); + if (biome == null) biome = world.getBiomeProvider().getBiome(chunkX * Chunk.CHUNK_SIZE_X + x, y >> 3, chunkZ * Chunk.CHUNK_SIZE_Z + z); // if thickness == -1, find what block to place on layer level based on biome if(currentLayerDepth == -1) { + topBlock = (short) biome.getSurfaceProperties().getTopBlock().id(); + fillerBlock = (short) biome.getSurfaceProperties().getFillerBlock().id(); + // if soil thickness is below 0, generate a stone basin where there is no top block layer if(soilThickness <= 0) { @@ -152,9 +175,9 @@ public class SurfaceGeneratorOverworld if(y >= minY + oceanY - 4 && y <= minY + oceanY + 1) { - // generate coastlines - topBlock = (short) biome.getSurfaceProperties().getTopBlock().id(); - fillerBlock = (short) biome.getSurfaceProperties().getFillerBlock().id(); + // // generate coastlines + // topBlock = (short) biome.getSurfaceProperties().getTopBlock().id(); + // fillerBlock = (short) biome.getSurfaceProperties().getFillerBlock().id(); if(biomeGeneratesMud) { topBlock = (short) Blocks.MUD.id(); @@ -199,68 +222,65 @@ public class SurfaceGeneratorOverworld continue; } - if (generateStoneVariants) + // begin stone layer pass when current surface layer has finished generating + if (generateStoneVariants && currentLayerDepth <= 0) { - // begin stone layer pass when current surface layer has finished generating - if(currentLayerDepth <= 0) + // basalt only generates at y 30 + the basalt thickness value at the current block level + a random number between 0 and 2 (gives the layer a random gradiant at the edge) + if (y >= (minY + basaltThicknessLevel - rand.nextInt(3)) && y <= (minY + 30 + basaltThicknessLevel - rand.nextInt(3))) { - // basalt only generates at y 30 + the basalt thickness value at the current block level + a random number between 0 and 2 (gives the layer a random gradiant at the edge) - if (y >= (minY + basaltThicknessLevel - rand.nextInt(3)) && y <= (minY + 30 + basaltThicknessLevel - rand.nextInt(3))) + if (generateBasaltLayer) { - if (generateBasaltLayer) - { - result.setBlock(x, y, z, Blocks.BASALT.id()); - continue; - } + result.setBlock(x, y, z, Blocks.BASALT.id()); + continue; } - if(biome == Biomes.OVERWORLD_GLACIER){ - if (y >= (minY + 56 + graniteThicknessLevel/4 - rand.nextInt(3)) && y <= maxY) - { - result.setBlock(x, y, z, Blocks.PERMAFROST.id()); - continue; - } + } + if(biome == Biomes.OVERWORLD_GLACIER){ + if (y >= (minY + 56 + graniteThicknessLevel/4 - rand.nextInt(3)) && y <= maxY) + { + result.setBlock(x, y, z, Blocks.PERMAFROST.id()); + continue; } - if(biome == Biomes.OVERWORLD_TUNDRA){ - if (y >= (minY + 56 + graniteThicknessLevel/4 - rand.nextInt(3)) && y <= (minY + (56 + 36) + graniteThicknessLevel/8 - rand.nextInt(3))) - { - result.setBlock(x, y, z, Blocks.PERMAFROST.id()); - continue; - } + } + if(biome == Biomes.OVERWORLD_TUNDRA){ + if (y >= (minY + 56 + graniteThicknessLevel/4 - rand.nextInt(3)) && y <= (minY + (56 + 36) + graniteThicknessLevel/8 - rand.nextInt(3))) + { + result.setBlock(x, y, z, Blocks.PERMAFROST.id()); + continue; } - if (y >= ((minY + 128 / 2) + graniteThicknessLevel - rand.nextInt(3)) && y <= (minY + 128 + graniteThicknessLevel - rand.nextInt(3))) + } + if (y >= ((minY + 128 / 2) + graniteThicknessLevel - rand.nextInt(3)) && y <= (minY + 128 + graniteThicknessLevel - rand.nextInt(3))) + { + if (generateGraniteLayer) { - if (generateGraniteLayer) - { - result.setBlock(x, y, z, Blocks.GRANITE.id()); - continue; - } + result.setBlock(x, y, z, Blocks.GRANITE.id()); + continue; } - if (y >= ((minY + 128 / 2) + limestoneThicknessLevel - rand.nextInt(3)) && y <= (minY + 128 + limestoneThicknessLevel - rand.nextInt(3))) + } + if (y >= ((minY + 128 / 2) + limestoneThicknessLevel - rand.nextInt(3)) && y <= (minY + 128 + limestoneThicknessLevel - rand.nextInt(3))) + { + if (generateLimestoneLayer) { - if (generateLimestoneLayer) - { - result.setBlock(x, y, z, Blocks.LIMESTONE.id()); - continue; - } + result.setBlock(x, y, z, Blocks.LIMESTONE.id()); + continue; } - continue; } + continue; } - // fill blocks with filler block until current layer level = -1 or air is encountered. - if (currentLayerDepth > 0) - { - currentLayerDepth--; + // fill blocks with filler block until current layer level == 0 or air is encountered. + for (; currentLayerDepth > 0; currentLayerDepth --, y--) { result.setBlock(x, y, z, fillerBlock); + if (result.getBlock(x, y, z) == 0) continue top_down; } // if reached end of filler layer, fill world with sandstone/permafrost if inside a glacier or desert biome - if(currentLayerDepth == 0) - { - SubSurfaceLayer subSurfaceLayer = getSubSurfaceLayer(biome, fillerBlock, y, minY, oceanY, rand); - if(subSurfaceLayer != null){ - currentLayerDepth = subSurfaceLayer.depth; - fillerBlock = subSurfaceLayer.block; + assert currentLayerDepth == 0; + final var subSurfaceLayer = getSubSurfaceLayer(biome, fillerBlock, y, minY, oceanY, rand); + if (subSurfaceLayer != null){ + fillerBlock = subSurfaceLayer.block; + for (currentLayerDepth = subSurfaceLayer.depth; currentLayerDepth > 0; currentLayerDepth --, y--) { + result.setBlock(x, y, z, fillerBlock); + if (result.getBlock(x, y, z) == 0) continue top_down; } } }