diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/NoiseChunkGenerator3DAddon.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/NoiseChunkGenerator3DAddon.java index dbb9cfcad..7f3190a79 100644 --- a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/NoiseChunkGenerator3DAddon.java +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/NoiseChunkGenerator3DAddon.java @@ -41,7 +41,7 @@ public class NoiseChunkGenerator3DAddon implements AddonInitializer { event.getPack() .getOrCreateRegistry(ChunkGeneratorProvider.class) .register(addon.key("NOISE_3D"), - pack -> new NoiseChunkGenerator3D(pack, platform, config.getElevationBlend(), config.getHorizontalRes(), + pack -> new NoiseChunkGenerator3D(platform, config.getElevationBlend(), config.getHorizontalRes(), config.getVerticalRes())); event.getPack() .applyLoader(SlantLayer.class, SlantLayer::new); diff --git a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/NoiseChunkGenerator3D.java b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/NoiseChunkGenerator3D.java index 429ec2164..41b832c5b 100644 --- a/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/NoiseChunkGenerator3D.java +++ b/common/addons/chunk-generator-noise-3d/src/main/java/com/dfsek/terra/addons/chunkgenerator/generation/NoiseChunkGenerator3D.java @@ -20,7 +20,6 @@ import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D; import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.SamplerProvider; import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.block.state.BlockState; -import com.dfsek.terra.api.config.ConfigPack; import com.dfsek.terra.api.profiler.ProfileFrame; import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; @@ -38,11 +37,9 @@ public class NoiseChunkGenerator3D implements ChunkGenerator { private final int carverHorizontalResolution; private final int carverVerticalResolution; - private final ConfigPack configPack; - public NoiseChunkGenerator3D(ConfigPack c, Platform platform, int elevationBlend, int carverHorizontalResolution, + public NoiseChunkGenerator3D(Platform platform, int elevationBlend, int carverHorizontalResolution, int carverVerticalResolution) { - this.configPack = c; this.platform = platform; this.air = platform.getWorldHandle().air(); this.carverHorizontalResolution = carverHorizontalResolution; @@ -131,6 +128,11 @@ public class NoiseChunkGenerator3D implements ChunkGenerator { } else return air; } + @Override + public Palette getPalette(int x, int y, int z, WorldProperties world, BiomeProvider biomeProvider) { + return biomeProvider.getBiome(x, z, world.getSeed()).getContext().get(PaletteInfo.class).paletteHolder().getPalette(y); + } + public SamplerProvider samplerProvider() { return samplerCache; } diff --git a/common/api/src/main/java/com/dfsek/terra/api/world/chunk/generation/ChunkGenerator.java b/common/api/src/main/java/com/dfsek/terra/api/world/chunk/generation/ChunkGenerator.java index afeeeb14d..c13adab9e 100644 --- a/common/api/src/main/java/com/dfsek/terra/api/world/chunk/generation/ChunkGenerator.java +++ b/common/api/src/main/java/com/dfsek/terra/api/world/chunk/generation/ChunkGenerator.java @@ -8,6 +8,7 @@ package com.dfsek.terra.api.world.chunk.generation; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.util.Palette; import com.dfsek.terra.api.world.info.WorldProperties; import org.jetbrains.annotations.NotNull; @@ -15,7 +16,6 @@ import org.jetbrains.annotations.NotNull; import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.util.vector.Vector3; import com.dfsek.terra.api.util.vector.Vector3Int; -import com.dfsek.terra.api.world.WritableWorld; public interface ChunkGenerator { @@ -31,4 +31,6 @@ public interface ChunkGenerator { default BlockState getBlock(WorldProperties world, Vector3Int vector3, BiomeProvider biomeProvider) { return getBlock(world, vector3.getX(), vector3.getY(), vector3.getZ(), biomeProvider); } + + Palette getPalette(int x, int y, int z, WorldProperties world, BiomeProvider biomeProvider); } diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/BeardGenerator.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/BeardGenerator.java new file mode 100644 index 000000000..bb504c85d --- /dev/null +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/BeardGenerator.java @@ -0,0 +1,178 @@ +package com.dfsek.terra.fabric.generation; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.ObjectListIterator; +import net.minecraft.block.BlockState; +import net.minecraft.structure.JigsawJunction; +import net.minecraft.structure.PoolStructurePiece; +import net.minecraft.structure.StructurePiece; +import net.minecraft.structure.StructureStart; +import net.minecraft.structure.pool.StructurePool.Projection; +import net.minecraft.util.Util; +import net.minecraft.util.math.BlockBox; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.gen.StructureAccessor; +import net.minecraft.world.gen.StructureWeightType; +import net.minecraft.world.gen.feature.StructureFeature; + +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; +import com.dfsek.terra.api.world.info.WorldProperties; + + +public class BeardGenerator { + private static final float[] STRUCTURE_WEIGHT_TABLE = Util.make(new float[13824], array -> { + for (int i = 0; i < 24; ++i) { + for (int j = 0; j < 24; ++j) { + for (int k = 0; k < 24; ++k) { + array[i * 24 * 24 + j * 24 + k] = (float) calculateStructureWeight(j - 12, k - 12, i - 12); + } + } + } + }); + private final ObjectList pieces; + private final ObjectList junctions; + private final ObjectListIterator pieceIterator; + private final ObjectListIterator junctionIterator; + private final Chunk chunk; + private final int minY; + private final int maxY; + + public BeardGenerator(StructureAccessor structureAccessor, Chunk chunk) { + this.chunk = chunk; + ChunkPos chunkPos = chunk.getPos(); + int i = chunkPos.getStartX(); + int j = chunkPos.getStartZ(); + this.junctions = new ObjectArrayList<>(32); + this.pieces = new ObjectArrayList<>(10); + int minY = chunk.getBottomY(); + int maxY = chunk.getTopY(); + for (StructureFeature structureFeature : StructureFeature.LAND_MODIFYING_STRUCTURES) { + for(StructureStart start : structureAccessor.getStructureStarts(ChunkSectionPos.from(chunk), structureFeature)) { + for(StructurePiece structurePiece : start.getChildren()) { + if(!structurePiece.intersectsChunk(chunkPos, 12)) continue; + if(structurePiece instanceof PoolStructurePiece poolStructurePiece) { + Projection projection = poolStructurePiece.getPoolElement().getProjection(); + if(projection == Projection.RIGID) { + this.pieces.add(poolStructurePiece); + } + for(JigsawJunction jigsawJunction : poolStructurePiece.getJunctions()) { + int k = jigsawJunction.getSourceX(); + int l = jigsawJunction.getSourceZ(); + if(k <= i - 12 || l <= j - 12 || k >= i + 15 + 12 || l >= j + 15 + 12) { + continue; + } + maxY = Math.max(maxY, jigsawJunction.getSourceGroundY()); + minY = Math.min(minY, jigsawJunction.getSourceGroundY()); + + this.junctions.add(jigsawJunction); + } + continue; + } + maxY = Math.max(maxY, structurePiece.getCenter().getY()); + minY = Math.min(minY, structurePiece.getCenter().getY()); + this.pieces.add(structurePiece); + } + } + } + this.pieceIterator = this.pieces.iterator(); + this.junctionIterator = this.junctions.iterator(); + this.minY = minY; + this.maxY = maxY; + } + + public void generate(ChunkGenerator generator, WorldProperties worldProperties, BiomeProvider biomeProvider) { + int xi = chunk.getPos().x << 4; + int zi = chunk.getPos().z << 4; + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + int depth = 0; + for(int y = maxY; y >= minY; y--) { + if(calculateNoise(x + xi, y, z + zi) > 0.5) { + chunk.setBlockState(new BlockPos(x, y, z), (BlockState) generator + .getPalette(x + xi, y, z + zi, worldProperties, biomeProvider) + .get(depth, x + xi, y, z + zi, worldProperties.getSeed()), false); + depth++; + } else { + depth = 0; + } + } + } + } + } + + + public double calculateNoise(int x, int y, int z) { + double noise = 0.0; + + while (this.pieceIterator.hasNext()) { + StructurePiece structurePiece = this.pieceIterator.next(); + BlockBox blockBox = structurePiece.getBoundingBox(); + int structureX = Math.max(0, Math.max(blockBox.getMinX() - x, x - blockBox.getMaxX())); + int structureY = y - (blockBox.getMinY() + (structurePiece instanceof PoolStructurePiece ? ((PoolStructurePiece)structurePiece).getGroundLevelDelta() : 0)); + int structureZ = Math.max(0, Math.max(blockBox.getMinZ() - z, z - blockBox.getMaxZ())); + StructureWeightType structureWeightType = structurePiece.getWeightType(); + if (structureWeightType == StructureWeightType.BURY) { + noise += getMagnitudeWeight(structureX, structureY, structureZ); + continue; + } + if (structureWeightType != StructureWeightType.BEARD) continue; + + noise += getStructureWeight(structureX, structureY, structureZ) * 0.8; + } + this.pieceIterator.back(this.pieces.size()); + while (this.junctionIterator.hasNext()) { + JigsawJunction structurePiece = this.junctionIterator.next(); + int structureX = x - structurePiece.getSourceX(); + int structureY = y - structurePiece.getSourceGroundY(); + int structureZ = z - structurePiece.getSourceZ(); + noise += getStructureWeight(structureX, structureY, structureZ) * 0.4; + } + this.junctionIterator.back(this.junctions.size()); + return noise; + } + + private static double getMagnitudeWeight(int x, int y, int z) { + double d = MathHelper.magnitude(x, (double)y / 2.0, z); + return MathHelper.clampedLerpFromProgress(d, 0.0, 6.0, 1.0, 0.0); + } + + /** + * Gets the structure weight from the array from the given position, or 0 if the position is out of bounds. + */ + private static double getStructureWeight(int x, int y, int z) { + int xOffset = x + 12; + int yOffset = y + 12; + int zOffset = z + 12; + if (xOffset < 0 || xOffset >= 24) { + return 0.0; + } + if (yOffset < 0 || yOffset >= 24) { + return 0.0; + } + if (zOffset < 0 || zOffset >= 24) { + return 0.0; + } + return STRUCTURE_WEIGHT_TABLE[zOffset * 24 * 24 + xOffset * 24 + yOffset]; + } + + + /** + * Calculates the structure weight for the given position. + *

The weight increases as x and z approach {@code (0, 0)}, and positive y values make the weight negative while negative y + * values make the weight positive. + */ + private static double calculateStructureWeight(int x, int y, int z) { + double horizontalDistanceSquared = x * x + z * z; + double yOffset = y + 0.5; + double verticalSquared = yOffset * yOffset; + double naturalDistance = Math.pow(Math.E, -(verticalSquared / 16.0 + horizontalDistanceSquared / 16.0)); + double inverseSquareRootDistance = -yOffset * MathHelper.fastInverseSqrt(verticalSquared / 2.0 + horizontalDistanceSquared / 2.0) / 2.0; + return inverseSquareRootDistance * naturalDistance; + } +} diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/FabricChunkGeneratorWrapper.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/FabricChunkGeneratorWrapper.java index 734bf0747..dc2326cbc 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/FabricChunkGeneratorWrapper.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/generation/FabricChunkGeneratorWrapper.java @@ -43,6 +43,7 @@ import net.minecraft.world.biome.source.util.MultiNoiseUtil; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.gen.GenerationStep; import net.minecraft.world.gen.StructureAccessor; +import net.minecraft.world.gen.StructureWeightSampler; import net.minecraft.world.gen.chunk.Blender; import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; import net.minecraft.world.gen.chunk.StructuresConfig; @@ -154,14 +155,14 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C @Override public CompletableFuture populateNoise(Executor executor, Blender arg, StructureAccessor structureAccessor, Chunk chunk) { + new StructureWeightSampler(structureAccessor, chunk); return CompletableFuture.supplyAsync(() -> { ProtoWorld world = (ProtoWorld) ((StructureAccessorAccessor) structureAccessor).getWorld(); - delegate.generateChunkData((ProtoChunk) chunk, world, pack.getBiomeProvider().caching(), chunk.getPos().x, chunk.getPos().z); - pack.getStages().forEach(populator -> { - if(populator instanceof Chunkified) { - populator.populate(world); - } - }); + BiomeProvider biomeProvider = pack.getBiomeProvider().caching(); + delegate.generateChunkData((ProtoChunk) chunk, world, biomeProvider, chunk.getPos().x, chunk.getPos().z); + + new BeardGenerator(structureAccessor, chunk).generate(delegate, world, biomeProvider); + return chunk; }, executor); } diff --git a/platforms/fabric/src/main/resources/terra.accesswidener b/platforms/fabric/src/main/resources/terra.accesswidener index 377aee552..a89875db1 100644 --- a/platforms/fabric/src/main/resources/terra.accesswidener +++ b/platforms/fabric/src/main/resources/terra.accesswidener @@ -1,3 +1,5 @@ accessWidener v1 named extendable method net/minecraft/client/world/GeneratorType (Ljava/lang/String;)V + +accessible method net/minecraft/world/gen/StructureWeightSampler (Lnet/minecraft/world/gen/StructureAccessor;Lnet/minecraft/world/chunk/Chunk;)V \ No newline at end of file