Improve Origin Hills tree generation and beach generation (#1688)

* Trees now follow Alpha Minecraft's generation gradient
* Beaches use Alpha's noise generation for sand and gravel
This commit is contained in:
SuperCoder79 2020-10-04 18:14:22 -04:00 committed by GitHub
parent f064126e70
commit 5741d6e98b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 312 additions and 37 deletions

View file

@ -0,0 +1,48 @@
package biomesoplenty.common.world;
import java.util.Arrays;
import java.util.Random;
public class AlphaOctavePerlinNoise
{
private AlphaPerlinNoise samplers[];
private int octaves;
public AlphaOctavePerlinNoise(Random random, int octaves)
{
this.octaves = octaves;
samplers = new AlphaPerlinNoise[octaves];
for(int i = 0; i < octaves; i++)
{
samplers[i] = new AlphaPerlinNoise(random);
}
}
public double sample(double x, double z)
{
double sum = 0.0;
double amplitude = 1.0;
for(int i = 0; i < this.octaves; i++)
{
sum += this.samplers[i].sample(x * amplitude, z * amplitude) / amplitude;
amplitude /= 2.0;
}
return sum;
}
public double sample(double x, double y, double z, double xFreq, double yFreq, double zFreq)
{
double sum = 0.0;
double amplitude = 1.0D;
for(int i = 0; i < octaves; i++)
{
sum += samplers[i].sample(x, y, z, xFreq * amplitude, yFreq * amplitude, zFreq * amplitude, amplitude);
amplitude /= 2D;
}
return sum;
}
}

View file

@ -0,0 +1,154 @@
package biomesoplenty.common.world;
import java.util.Random;
public class AlphaPerlinNoise
{
private final int[] permutations;
public final double offsetX;
public final double offsetY;
public final double offsetZ;
public AlphaPerlinNoise(Random random)
{
permutations = new int[512];
offsetX = random.nextDouble() * 256D;
offsetY = random.nextDouble() * 256D;
offsetZ = random.nextDouble() * 256D;
for(int i = 0; i < 256; i++)
{
permutations[i] = i;
}
for(int j = 0; j < 256; j++)
{
int k = random.nextInt(256 - j) + j;
int l = permutations[j];
permutations[j] = permutations[k];
permutations[k] = l;
permutations[j + 256] = permutations[j];
}
}
public double sample(double x, double y, double z)
{
double localX = x + offsetX;
double localY = y + offsetY;
double localZ = z + offsetZ;
int floorX = (int) localX;
int floorY = (int) localY;
int floorZ = (int) localZ;
if(localX < (double) floorX)
{
floorX--;
}
if(localY < (double) floorY)
{
floorY--;
}
if(localZ < (double) floorZ)
{
floorZ--;
}
int maskedX = floorX & 0xff;
int maskedY = floorY & 0xff;
int maskedZ = floorZ & 0xff;
localX -= floorX;
localY -= floorY;
localZ -= floorZ;
// Apply smoothstep on all axes
double smoothedX = localX * localX * localX * (localX * (localX * 6 - 15) + 10);
double smoothedY = localY * localY * localY * (localY * (localY * 6 - 15) + 10);
double smoothedZ = localZ * localZ * localZ * (localZ * (localZ * 6 - 15) + 10);
int perm1 = permutations[maskedX] + maskedY;
int perm2 = permutations[perm1] + maskedZ;
int perm3 = permutations[perm1 + 1] + maskedZ;
int perm4 = permutations[maskedX + 1] + maskedY;
int perm5 = permutations[perm4] + maskedZ;
int perm6 = permutations[perm4 + 1] + maskedZ;
// Apply trilinear interpolation on the noise to get the final result
return lerp(smoothedZ,
lerp(smoothedY,
lerp(smoothedX,
grad(permutations[perm2], localX, localY, localZ),
grad(permutations[perm5], localX - 1.0D, localY, localZ)),
lerp(smoothedX,
grad(permutations[perm3], localX, localY - 1.0D, localZ),
grad(permutations[perm6], localX - 1.0D, localY - 1.0D, localZ))),
lerp(smoothedY,
lerp(smoothedX,
grad(permutations[perm2 + 1], localX, localY, localZ - 1.0D),
grad(permutations[perm5 + 1], localX - 1.0D, localY, localZ - 1.0D)),
lerp(smoothedX,
grad(permutations[perm3 + 1], localX, localY - 1.0D, localZ - 1.0D),
grad(permutations[perm6 + 1], localX - 1.0D, localY - 1.0D, localZ - 1.0D))));
}
public double lerp(double delta, double start, double end)
{
return start + delta * (end - start);
}
public double grad(int i, double d, double d1, double d2)
{
int j = i & 0xf;
double d3 = j >= 8 ? d1 : d;
double d4 = j >= 4 ? j != 12 && j != 14 ? d2 : d : d1;
return ((j & 1) != 0 ? -d3 : d3) + ((j & 2) != 0 ? -d4 : d4);
}
// Note: Passing in the z value as the y coordinate is intended behavior here.
public double sample(double x, double z)
{
return sample(x, z, 0.0);
}
// Alternate sample method that provides frequency and amplitude modification functionality
public double sample(double x, double y, double z, double freqX, double freqY, double freqZ, double amplitude)
{
double noiseAmplitude = 1.0 / amplitude;
double localX = (x + (double) 0) * freqX + offsetX;
int floorX = (int) localX;
if(localX < (double) floorX)
{
floorX--;
}
int maskX = floorX & 0xff;
localX -= floorX;
double smoothedX = localX * localX * localX * (localX * (localX * 6D - 15D) + 10D);
double localZ = (z + (double) 0) * freqZ + offsetZ;
int floorZ = (int) localZ;
if(localZ < (double) floorZ)
{
floorZ--;
}
int maskZ = floorZ & 0xff;
localZ -= floorZ;
double smoothZ = localZ * localZ * localZ * (localZ * (localZ * 6D - 15D) + 10D);
double localY = (y + (double) 0) * freqY + offsetY;
int floorY = (int) localY;
if(localY < (double) floorY)
{
floorY--;
}
int maskY = floorY & 0xff;
localY -= floorY;
double smoothY = localY * localY * localY * (localY * (localY * 6D - 15D) + 10D);
int perm1 = permutations[maskX] + maskY;
int perm2 = permutations[perm1] + maskZ;
int perm3 = permutations[perm1 + 1] + maskZ;
int perm4 = permutations[maskX + 1] + maskY;
int perm5 = permutations[perm4] + maskZ;
int perm6 = permutations[perm4 + 1] + maskZ;
double lerp1 = lerp(smoothedX, grad(permutations[perm2], localX, localY, localZ), grad(permutations[perm5], localX - 1.0D, localY, localZ));
double lerp2 = lerp(smoothedX, grad(permutations[perm3], localX, localY - 1.0D, localZ), grad(permutations[perm6], localX - 1.0D, localY - 1.0D, localZ));
double lerp3 = lerp(smoothedX, grad(permutations[perm2 + 1], localX, localY, localZ - 1.0D), grad(permutations[perm5 + 1], localX - 1.0D, localY, localZ - 1.0D));
double lerp4 = lerp(smoothedX, grad(permutations[perm3 + 1], localX, localY - 1.0D, localZ - 1.0D), grad(permutations[perm6 + 1], localX - 1.0D, localY - 1.0D, localZ - 1.0D));
double biLerp1 = lerp(smoothY, lerp1, lerp2);
double biLerp2 = lerp(smoothY, lerp3, lerp4);
double finalNoise = lerp(smoothZ, biLerp1, biLerp2);
return finalNoise * noiseAmplitude;
}
}

View file

@ -10,6 +10,7 @@ package biomesoplenty.common.world.gen.feature;
import biomesoplenty.api.block.BOPBlocks;
import biomesoplenty.common.util.biome.FeatureUtil;
import biomesoplenty.common.world.biome.BiomeFeatureHelper;
import biomesoplenty.common.world.gen.placement.BOPPlacements;
import biomesoplenty.core.BiomesOPlenty;
import com.google.common.collect.ImmutableList;
import net.minecraft.block.Blocks;
@ -22,6 +23,7 @@ import net.minecraft.world.gen.blockstateprovider.SimpleBlockStateProvider;
import net.minecraft.world.gen.feature.*;
import net.minecraft.world.gen.placement.AtSurfaceWithExtraConfig;
import net.minecraft.world.gen.placement.ChanceConfig;
import net.minecraft.world.gen.placement.NoPlacementConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraft.world.gen.placement.TopSolidRangeConfig;
@ -136,7 +138,7 @@ public class BOPConfiguredFeatures
public static final ConfiguredFeature<?, ?> OASIS_TREES = register("oasis_trees", Feature.RANDOM_SELECTOR.configured(new MultipleRandomFeatureConfig(ImmutableList.of(PALM_TREE.weighted(0.3F)), OASIS_JUNGLE_TWIGLET_TREE)).decorated(Features.Placements.HEIGHTMAP_SQUARE).decorated(Placement.COUNT_EXTRA.configured(new AtSurfaceWithExtraConfig(5, 0.2F, 1))));
public static final ConfiguredFeature<?, ?> OMINOUS_WOODS_TREES = register("ominous_woods_trees", Feature.RANDOM_SELECTOR.configured(new MultipleRandomFeatureConfig(ImmutableList.of(DEAD_TREE.weighted(0.05F), DYING_TREE.weighted(0.15F), WILLOW_TREE.weighted(0.15F), TALL_UMBRAN_TREE.weighted(0.7F)), UMBRAN_TREE)).decorated(Features.Placements.HEIGHTMAP_SQUARE).decorated(Placement.COUNT_EXTRA.configured(new AtSurfaceWithExtraConfig(7, 0.5F, 1))));
public static final ConfiguredFeature<?, ?> ORCHARD_TREES = register("orchard_trees", Feature.RANDOM_SELECTOR.configured(new MultipleRandomFeatureConfig(ImmutableList.of(BIG_FLOWERING_OAK_TREE.weighted(0.1F)), FLOWERING_OAK_TREE)).decorated(Features.Placements.HEIGHTMAP_SQUARE).decorated(Placement.COUNT_EXTRA.configured(new AtSurfaceWithExtraConfig(2, 0.3F, 1))));
public static final ConfiguredFeature<?, ?> ORIGIN_HILLS_TREES = register("origin_hills_trees", Feature.RANDOM_SELECTOR.configured(new MultipleRandomFeatureConfig(ImmutableList.of(BIG_ORIGIN_TREE.weighted(0.1F)), ORIGIN_TREE)).decorated(Features.Placements.HEIGHTMAP_SQUARE).decorated(Placement.COUNT_EXTRA.configured(new AtSurfaceWithExtraConfig(3, 0.4F, 1))));
public static final ConfiguredFeature<?, ?> ORIGIN_HILLS_TREES = register("origin_hills_trees", Feature.RANDOM_SELECTOR.configured(new MultipleRandomFeatureConfig(ImmutableList.of(BIG_ORIGIN_TREE.weighted(0.1F)), ORIGIN_TREE)).decorated(Features.Placements.HEIGHTMAP_SQUARE).decorated(BOPPlacements.ALPHA_TREE.configured(NoPlacementConfig.INSTANCE)));
public static final ConfiguredFeature<?, ?> OUTBACK_TREES = register("outback_trees", Feature.RANDOM_SELECTOR.configured(new MultipleRandomFeatureConfig(ImmutableList.of(ACACIA_BUSH.weighted(0.1F)), ACACIA_TWIGLET_TREE)).decorated(Features.Placements.HEIGHTMAP_SQUARE).decorated(Placement.COUNT_EXTRA.configured(new AtSurfaceWithExtraConfig((int)0.5F, 0.3F, 1))));
public static final ConfiguredFeature<?, ?> OVERGROWN_CLIFFS_TREES = register("overgrown_cliffs_trees", Feature.RANDOM_SELECTOR.configured(new MultipleRandomFeatureConfig(ImmutableList.of(JUNGLE_TWIGLET_TREE.weighted(0.3F), MAHOGANY_TREE.weighted(0.1F)), BUSH)).decorated(Features.Placements.HEIGHTMAP_SQUARE).decorated(Placement.COUNT_EXTRA.configured(new AtSurfaceWithExtraConfig(16, 0.4F, 1))));
public static final ConfiguredFeature<?, ?> PRAIRIE_TREES = register("prairie_trees", Feature.RANDOM_SELECTOR.configured(new MultipleRandomFeatureConfig(ImmutableList.of(BIG_OAK_TREE.weighted(0.1F)), SPARSE_OAK_TREE)).decorated(Features.Placements.HEIGHTMAP_SQUARE).decorated(Placement.COUNT_EXTRA.configured(new AtSurfaceWithExtraConfig((int)0.85F, 0.3F, 1))));

View file

@ -0,0 +1,46 @@
package biomesoplenty.common.world.gen.placement;
import java.util.Random;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import biomesoplenty.common.world.AlphaOctavePerlinNoise;
import com.mojang.serialization.Codec;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.gen.feature.WorldDecoratingHelper;
import net.minecraft.world.gen.placement.NoPlacementConfig;
import net.minecraft.world.gen.placement.Placement;
public class AlphaTreePlacement extends Placement<NoPlacementConfig>
{
private AlphaOctavePerlinNoise treeNoise;
private long seed;
public AlphaTreePlacement(Codec<NoPlacementConfig> codec)
{
super(codec);
}
@Override
public Stream<BlockPos> getPositions(WorldDecoratingHelper decoratorContext, Random random, NoPlacementConfig config, BlockPos pos)
{
// If the seed has changed, then re-initialize the noise.
long seed = decoratorContext.level.getSeed();
if (this.treeNoise == null || seed != this.seed) {
this.treeNoise = new AlphaOctavePerlinNoise(new Random(seed), 8);
this.seed = seed;
}
// Sample the tree gen noise for the base amount of trees in this chunk
int treeCount = (int)((this.treeNoise.sample((double) pos.getX() * 0.5D, (double) pos.getZ() * 0.5D) / 8.0D + random.nextDouble() * 4.0D + 4.0D) / 3.0D);
// Add extra trees randomly
if (random.nextInt(10) == 0) {
treeCount++;
}
// Map to position
return IntStream.range(0, treeCount).mapToObj((idx) -> pos);
}
}

View file

@ -18,6 +18,7 @@ import net.minecraft.world.gen.placement.*;
public class BOPPlacements
{
public static final Placement<DenseFeatureSpreadConfig> COUNT = register("count", new BOPCountPlacement(DenseFeatureSpreadConfig.CODEC));
public static final Placement<NoPlacementConfig> ALPHA_TREE = register("alpha_tree", new AlphaTreePlacement(NoPlacementConfig.CODEC));
private static <T extends IPlacementConfig, G extends Placement<T>> G register(String key, G placement)
{

View file

@ -1,18 +1,26 @@
package biomesoplenty.common.world.gen.surfacebuilders;
import biomesoplenty.common.world.AlphaOctavePerlinNoise;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Codec;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.util.SharedSeedRandom;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.PerlinNoiseGenerator;
import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder;
import net.minecraft.world.gen.surfacebuilders.SurfaceBuilderConfig;
import java.util.Random;
import java.util.stream.IntStream;
public class OriginHillsSurfaceBuilder extends SurfaceBuilder<SurfaceBuilderConfig>
{
protected long seed;
protected AlphaOctavePerlinNoise sandNoise;
protected AlphaOctavePerlinNoise gravelNoise;
public OriginHillsSurfaceBuilder(Codec<SurfaceBuilderConfig> p_i232124_1_)
{
super(p_i232124_1_);
@ -23,82 +31,96 @@ public class OriginHillsSurfaceBuilder extends SurfaceBuilder<SurfaceBuilderConf
}
protected void apply(Random random, IChunk chunkIn, Biome biomeIn, int x, int z, int startHeight, double noise, BlockState defaultBlock, BlockState defaultFluid, BlockState top, BlockState middle, BlockState bottom, int sealevel) {
BlockState blockstate = top;
BlockState blockstate1 = middle;
BlockPos.Mutable blockpos$mutable = new BlockPos.Mutable();
BlockState topState = top;
BlockState middleState = middle;
BlockPos.Mutable mutable = new BlockPos.Mutable();
int i = -1;
int j = (int)(noise / 3.0D + 3.0D + random.nextDouble() * 0.25D);
int k = x & 15;
int l = z & 15;
int placedDepth = -1;
int grassDepth = (int)(noise / 3.0D + 3.0D + random.nextDouble() * 0.25D);
int localX = x & 15;
int localZ = z & 15;
boolean gravelGen = noise + random.nextDouble() * 0.20000000000000001D < -2.0D;
boolean sandGen = noise + random.nextDouble() * 0.20000000000000001D > 1.5D;
boolean gravelGen = gravelNoise.sample(x, 109.0134, z, 0.03125, 1, 0.03125) + random.nextDouble() * 0.2 > 3.0;
boolean sandGen = sandNoise.sample(x, z, 0, 0.03125, 0.03125, 1) + random.nextDouble() * 0.2 > 0.0;
for (int i1 = startHeight; i1 >= 0; --i1)
for (int y = startHeight; y >= 0; --y)
{
blockpos$mutable.set(k, i1, l);
BlockState blockstate2 = chunkIn.getBlockState(blockpos$mutable);
mutable.set(localX, y, localZ);
BlockState blockstate2 = chunkIn.getBlockState(mutable);
if (blockstate2.isAir())
{
i = -1;
placedDepth = -1;
}
else if (blockstate2.is(defaultBlock.getBlock()))
{
if (i == -1)
if (placedDepth == -1)
{
if (j <= 0)
if (grassDepth <= 0)
{
blockstate = Blocks.AIR.defaultBlockState();
blockstate1 = defaultBlock;
topState = Blocks.AIR.defaultBlockState();
middleState = defaultBlock;
}
else if (i1 >= sealevel - 4 && i1 <= sealevel + 1)
else if (y >= sealevel - 4 && y <= sealevel + 1)
{
blockstate = top;
blockstate1 = middle;
topState = top;
middleState = middle;
if (gravelGen)
{
blockstate = Blocks.AIR.defaultBlockState();
blockstate1 = Blocks.GRAVEL.defaultBlockState();
topState = Blocks.AIR.defaultBlockState();
middleState = Blocks.GRAVEL.defaultBlockState();
}
if (sandGen)
{
blockstate = Blocks.SAND.defaultBlockState();
blockstate1 = Blocks.SAND.defaultBlockState();
topState = Blocks.SAND.defaultBlockState();
middleState = Blocks.SAND.defaultBlockState();
}
}
if (i1 < sealevel && (blockstate == null || blockstate.isAir()))
if (y < sealevel && (topState == null || topState.isAir()))
{
if (biomeIn.getTemperature(blockpos$mutable.set(x, i1, z)) < 0.15F)
if (biomeIn.getTemperature(mutable.set(x, y, z)) < 0.15F)
{
blockstate = Blocks.ICE.defaultBlockState();
topState = Blocks.ICE.defaultBlockState();
}
else
{
blockstate = defaultFluid;
topState = defaultFluid;
}
blockpos$mutable.set(k, i1, l);
mutable.set(localX, y, localZ);
}
i = j;
if (i1 >= sealevel - 1)
placedDepth = grassDepth;
if (y >= sealevel - 1)
{
chunkIn.setBlockState(blockpos$mutable, blockstate, false);
chunkIn.setBlockState(mutable, topState, false);
}
else
{
chunkIn.setBlockState(blockpos$mutable, blockstate1, false);
chunkIn.setBlockState(mutable, middleState, false);
}
}
else if (i > 0)
else if (placedDepth > 0)
{
--i;
chunkIn.setBlockState(blockpos$mutable, blockstate1, false);
--placedDepth;
chunkIn.setBlockState(mutable, middleState, false);
}
}
}
}
@Override
public void initNoise(long seed)
{
// If the seed has changed, then re-initialize the noise.
if (this.seed != seed || this.sandNoise == null || this.gravelNoise == null)
{
SharedSeedRandom random = new SharedSeedRandom(seed);
this.sandNoise = new AlphaOctavePerlinNoise(random, 4);
this.gravelNoise = new AlphaOctavePerlinNoise(random, 4);
}
this.seed = seed;
}
}

View file

@ -37,3 +37,5 @@ public-f net.minecraft.item.HoeItem field_195973_b # TILLABLES
public-f net.minecraft.item.ShovelItem field_195955_e # FLATTENABLES
protected net.minecraft.entity.item.BoatEntity func_184447_s()V #tickLerp
public net.minecraft.world.gen.feature.WorldDecoratingHelper *