mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-02-16 10:30:42 +00:00
redo biome blending
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package com.dfsek.terra.api.util.mutable;
|
||||
|
||||
public class MutableInteger extends Number implements MutablePrimitive<Integer> {
|
||||
private int value;
|
||||
|
||||
public MutableInteger() {
|
||||
|
||||
}
|
||||
|
||||
public MutableInteger(int init) {
|
||||
this.value = init;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void add() {
|
||||
add(1);
|
||||
}
|
||||
|
||||
public void add(int add) {
|
||||
this.value += add;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.dfsek.terra.api.util.mutable;
|
||||
|
||||
public interface MutablePrimitive<T> {
|
||||
T get();
|
||||
|
||||
void set(T value);
|
||||
}
|
||||
@@ -13,10 +13,14 @@ public interface Generator {
|
||||
* @param z - The z coordinate.
|
||||
* @return double - Noise value at the specified coordinates.
|
||||
*/
|
||||
double getNoise(int x, int y, int z);
|
||||
double getNoise(double x, double y, double z);
|
||||
|
||||
double getElevation(int x, int z);
|
||||
|
||||
int getBlendDistance();
|
||||
|
||||
double getWeight();
|
||||
|
||||
/**
|
||||
* Gets the BlocPalette to generate the biome with.
|
||||
*
|
||||
|
||||
@@ -2,18 +2,15 @@ package com.dfsek.terra.config.base;
|
||||
|
||||
import com.dfsek.tectonic.annotations.Default;
|
||||
import com.dfsek.tectonic.annotations.Value;
|
||||
import com.dfsek.tectonic.config.ValidatedConfigTemplate;
|
||||
import com.dfsek.tectonic.exception.ValidationException;
|
||||
import com.dfsek.terra.api.math.MathUtil;
|
||||
import com.dfsek.tectonic.config.ConfigTemplate;
|
||||
import com.dfsek.terra.biome.BiomeProvider;
|
||||
import com.dfsek.terra.generation.config.NoiseBuilder;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings({"unused", "FieldMayBeFinal"})
|
||||
public class ConfigPackTemplate implements ValidatedConfigTemplate {
|
||||
public class ConfigPackTemplate implements ConfigTemplate {
|
||||
@Value("id")
|
||||
private String id;
|
||||
|
||||
@@ -36,10 +33,6 @@ public class ConfigPackTemplate implements ValidatedConfigTemplate {
|
||||
@Default
|
||||
private double blendAmp = 4.0D;
|
||||
|
||||
@Value("blend.terrain.base")
|
||||
@Default
|
||||
private int baseBlend = 4;
|
||||
|
||||
@Value("structures.locatable")
|
||||
@Default
|
||||
private Map<String, String> locatable = new HashMap<>();
|
||||
@@ -167,9 +160,6 @@ public class ConfigPackTemplate implements ValidatedConfigTemplate {
|
||||
return erodeOctaves;
|
||||
}
|
||||
|
||||
public int getBaseBlend() {
|
||||
return baseBlend;
|
||||
}
|
||||
|
||||
public int getElevationBlend() {
|
||||
return elevationBlend;
|
||||
@@ -178,12 +168,4 @@ public class ConfigPackTemplate implements ValidatedConfigTemplate {
|
||||
public Map<String, String> getLocatable() {
|
||||
return locatable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() throws ValidationException {
|
||||
if(!MathUtil.equals(FastMath.log(baseBlend) / FastMath.log(2d), FastMath.round(FastMath.log(baseBlend) / FastMath.log(2d)))) {
|
||||
throw new ValidationException("Biome base blend value \"" + baseBlend + "\" is not a power of 2.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,13 +36,19 @@ public class GeneratorBuilder {
|
||||
|
||||
private double elevationWeight;
|
||||
|
||||
private int blendDistance;
|
||||
|
||||
|
||||
public WorldGenerator build(long seed) {
|
||||
synchronized(gens) {
|
||||
return gens.computeIfAbsent(seed, k -> new WorldGenerator(seed, noiseEquation, elevationEquation, varScope, noiseBuilderMap, palettes, slantPalettes, interpolateElevation, noise2d, base, biomeNoise.build((int) seed), elevationWeight));
|
||||
return gens.computeIfAbsent(seed, k -> new WorldGenerator(seed, noiseEquation, elevationEquation, varScope, noiseBuilderMap, palettes, slantPalettes, noise2d, base, biomeNoise.build((int) seed), elevationWeight, blendDistance));
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlendDistance(int blendDistance) {
|
||||
this.blendDistance = blendDistance;
|
||||
}
|
||||
|
||||
public void setBiomeNoise(NoiseBuilder biomeNoise) {
|
||||
this.biomeNoise = biomeNoise;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public class BiomeFactory implements TerraFactory<BiomeTemplate, TerraBiome> {
|
||||
generatorBuilder.setBase(template.getNoise2dBase());
|
||||
generatorBuilder.setElevationWeight(template.getElevationWeight());
|
||||
generatorBuilder.setBiomeNoise(template.getBiomeNoise());
|
||||
generatorBuilder.setBlendDistance(template.getBlendDistance());
|
||||
|
||||
|
||||
return new UserDefinedBiome(template.getVanilla(), generatorBuilder, template);
|
||||
|
||||
@@ -70,6 +70,11 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
|
||||
@Abstractable
|
||||
private NoiseBuilder biomeNoise = new NoiseBuilder();
|
||||
|
||||
@Value("blend.distance")
|
||||
@Abstractable
|
||||
@Default
|
||||
private int blendDistance = 3;
|
||||
|
||||
@Value("erode")
|
||||
@Abstractable
|
||||
@Default
|
||||
@@ -165,6 +170,10 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
|
||||
return color;
|
||||
}
|
||||
|
||||
public int getBlendDistance() {
|
||||
return blendDistance;
|
||||
}
|
||||
|
||||
public boolean interpolateElevation() {
|
||||
return interpolateElevation;
|
||||
}
|
||||
|
||||
@@ -29,21 +29,21 @@ public class WorldGenerator implements Generator {
|
||||
private final Variable zVar;
|
||||
private final Variable elevationXVar;
|
||||
private final Variable elevationZVar;
|
||||
private final boolean elevationInterpolation;
|
||||
private final boolean noise2d;
|
||||
private final double base;
|
||||
private final NoiseSampler biomeNoise;
|
||||
private final double elevationWeight;
|
||||
private final int blendDistance;
|
||||
|
||||
public WorldGenerator(long seed, String equation, String elevateEquation, Scope vScope, Map<String, NoiseBuilder> noiseBuilders, PaletteHolder palettes, PaletteHolder slantPalettes, boolean elevationInterpolation, boolean noise2d, double base, NoiseSampler biomeNoise, double elevationWeight) {
|
||||
public WorldGenerator(long seed, String equation, String elevateEquation, Scope vScope, Map<String, NoiseBuilder> noiseBuilders, PaletteHolder palettes, PaletteHolder slantPalettes, boolean noise2d, double base, NoiseSampler biomeNoise, double elevationWeight, int blendDistance) {
|
||||
this.palettes = palettes;
|
||||
this.slantPalettes = slantPalettes;
|
||||
|
||||
this.elevationInterpolation = elevationInterpolation;
|
||||
this.noise2d = noise2d;
|
||||
this.base = base;
|
||||
this.biomeNoise = biomeNoise;
|
||||
this.elevationWeight = elevationWeight;
|
||||
this.blendDistance = blendDistance;
|
||||
|
||||
Parser p = new Parser();
|
||||
p.registerFunction("rand", new RandomFunction());
|
||||
@@ -96,7 +96,17 @@ public class WorldGenerator implements Generator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized double getNoise(int x, int y, int z) {
|
||||
public int getBlendDistance() {
|
||||
return blendDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWeight() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized double getNoise(double x, double y, double z) {
|
||||
xVar.setValue(x);
|
||||
if(!noise2d) yVar.setValue(y);
|
||||
zVar.setValue(z);
|
||||
|
||||
@@ -10,8 +10,8 @@ public class Sampler {
|
||||
private final ChunkInterpolator interpolator;
|
||||
private final ElevationInterpolator elevationInterpolator;
|
||||
|
||||
public Sampler(int x, int z, BiomeProvider provider, World world, int elevationSmooth, int generationSmooth) {
|
||||
this.interpolator = new ChunkInterpolator(world, x, z, provider, generationSmooth);
|
||||
public Sampler(int x, int z, BiomeProvider provider, World world, int elevationSmooth) {
|
||||
this.interpolator = new ChunkInterpolator(world, x, z, provider);
|
||||
this.elevationInterpolator = new ElevationInterpolator(world, x, z, provider, elevationSmooth);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public class SamplerCache {
|
||||
public Sampler load(@NotNull Long key) {
|
||||
int cx = (int) (key >> 32);
|
||||
int cz = (int) key.longValue();
|
||||
return new Sampler(cx, cz, terraWorld.getBiomeProvider(), world, terraWorld.getConfig().getTemplate().getElevationBlend(), terraWorld.getConfig().getTemplate().getBaseBlend());
|
||||
return new Sampler(cx, cz, terraWorld.getBiomeProvider(), world, terraWorld.getConfig().getTemplate().getElevationBlend());
|
||||
}
|
||||
});
|
||||
terraWorld = main.getWorld(world);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
package com.dfsek.terra.generation.math.interpolation;
|
||||
|
||||
import com.dfsek.terra.api.platform.world.World;
|
||||
import com.dfsek.terra.api.util.mutable.MutableInteger;
|
||||
import com.dfsek.terra.api.world.biome.Generator;
|
||||
import com.dfsek.terra.biome.BiomeProvider;
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class to abstract away the 16 Interpolators needed to generate a chunk.<br>
|
||||
* Contains method to get interpolated noise at a coordinate within the chunk.
|
||||
*/
|
||||
public class ChunkInterpolator {
|
||||
private final Interpolator3[][][] interpGrid = new Interpolator3[4][64][4];
|
||||
private final Generator[][] gens = new Generator[7][7];
|
||||
private final boolean[][] needsBiomeInterp = new boolean[5][5];
|
||||
private final double[][][] noiseStorage = new double[7][7][65];
|
||||
private final int smooth;
|
||||
|
||||
/**
|
||||
* Instantiates a 3D ChunkInterpolator at a pair of chunk coordinates.
|
||||
@@ -23,36 +23,34 @@ public class ChunkInterpolator {
|
||||
* @param chunkZ Z coordinate of the chunk.
|
||||
* @param provider BiomeGrid to use for noise fetching.
|
||||
*/
|
||||
public ChunkInterpolator(World w, int chunkX, int chunkZ, BiomeProvider provider, int smooth) {
|
||||
public ChunkInterpolator(World w, int chunkX, int chunkZ, BiomeProvider provider) {
|
||||
Generator[][] gens = new Generator[5][5];
|
||||
int xOrigin = chunkX << 4;
|
||||
int zOrigin = chunkZ << 4;
|
||||
this.smooth = smooth;
|
||||
|
||||
for(int x = -1; x < 6; x++) {
|
||||
for(int z = -1; z < 6; z++) {
|
||||
gens[x + 1][z + 1] = provider.getBiome(xOrigin + (x * smooth), zOrigin + (z * smooth)).getGenerator(w);
|
||||
}
|
||||
}
|
||||
|
||||
for(int x = 0; x < 5; x++) {
|
||||
for(int z = 0; z < 5; z++) {
|
||||
needsBiomeInterp[x][z] = compareGens(x + 1, z + 1);
|
||||
gens[x][z] = provider.getBiome(xOrigin + (x * 4), zOrigin + (z * 4)).getGenerator(w);
|
||||
}
|
||||
}
|
||||
|
||||
for(byte x = -1; x < 6; x++) {
|
||||
for(byte z = -1; z < 6; z++) {
|
||||
Generator generator = gens[x + 1][z + 1];
|
||||
if(generator.is2d()) {
|
||||
double n = generator.getNoise((x * smooth) + xOrigin, 0, (z * smooth) + zOrigin);
|
||||
for(int y = 0; y < 65; y++) {
|
||||
noiseStorage[x + 1][z + 1][y] = n + noise2dExtrude(y << 2, generator.get2dBase());
|
||||
}
|
||||
} else {
|
||||
for(int y = 0; y < 65; y++) {
|
||||
noiseStorage[x + 1][z + 1][y] = generator.getNoise((x * smooth) + xOrigin, y << 2, (z * smooth) + zOrigin);
|
||||
double[][][] noiseStorage = new double[5][5][65];
|
||||
|
||||
for(int x = 0; x < 5; x++) {
|
||||
for(int z = 0; z < 5; z++) {
|
||||
Generator generator = gens[x][z];
|
||||
Map<Generator, MutableInteger> genMap = new HashMap<>();
|
||||
|
||||
int blend = generator.getBlendDistance();
|
||||
for(int xi = -blend; xi <= blend; xi++) {
|
||||
for(int zi = -blend; zi <= blend; zi++) {
|
||||
genMap.computeIfAbsent(provider.getBiome(xOrigin + ((x + xi) << 2), zOrigin + ((z + zi) << 2)).getGenerator(w), g -> new MutableInteger(0)).add(); // Increment by 1
|
||||
}
|
||||
}
|
||||
|
||||
for(int y = 0; y < 65; y++) {
|
||||
noiseStorage[x][z][y] = computeNoise(genMap, (x << 2) + xOrigin, y << 2, (z << 2) + zOrigin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,54 +58,48 @@ public class ChunkInterpolator {
|
||||
for(byte z = 0; z < 4; z++) {
|
||||
for(int y = 0; y < 64; y++) {
|
||||
interpGrid[x][y][z] = new Interpolator3(
|
||||
biomeAvg(x, y, z),
|
||||
biomeAvg(x + 1, y, z),
|
||||
biomeAvg(x, y + 1, z),
|
||||
biomeAvg(x + 1, y + 1, z),
|
||||
biomeAvg(x, y, z + 1),
|
||||
biomeAvg(x + 1, y, z + 1),
|
||||
biomeAvg(x, y + 1, z + 1),
|
||||
biomeAvg(x + 1, y + 1, z + 1));
|
||||
biomeAvg(x, y, z, noiseStorage),
|
||||
biomeAvg(x + 1, y, z, noiseStorage),
|
||||
biomeAvg(x, y + 1, z, noiseStorage),
|
||||
biomeAvg(x + 1, y + 1, z, noiseStorage),
|
||||
biomeAvg(x, y, z + 1, noiseStorage),
|
||||
biomeAvg(x + 1, y, z + 1, noiseStorage),
|
||||
biomeAvg(x, y + 1, z + 1, noiseStorage),
|
||||
biomeAvg(x + 1, y + 1, z + 1, noiseStorage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double computeNoise(Map<Generator, MutableInteger> gens, double x, double y, double z) {
|
||||
double n = 0;
|
||||
double div = 0;
|
||||
for(Map.Entry<Generator, MutableInteger> entry : gens.entrySet()) {
|
||||
Generator gen = entry.getKey();
|
||||
int weight = entry.getValue().get();
|
||||
double noise = computeNoise(gen, x, y, z);
|
||||
|
||||
n += noise * weight;
|
||||
div += gen.getWeight() * weight;
|
||||
}
|
||||
return n / div;
|
||||
}
|
||||
|
||||
private static double computeNoise(Generator generator, double x, double y, double z) {
|
||||
if(generator.is2d()) return generator.getNoise(x, 0, z) + noise2dExtrude(y, generator.get2dBase());
|
||||
else return generator.getNoise(x, y, z);
|
||||
}
|
||||
|
||||
private static int reRange(int value, int high) {
|
||||
return FastMath.max(FastMath.min(value, high), 0);
|
||||
}
|
||||
|
||||
private boolean compareGens(int x, int z) {
|
||||
Generator comp = gens[x][z];
|
||||
if(!comp.equals(gens[x + 1][z])) return true;
|
||||
|
||||
if(!comp.equals(gens[x][z + 1])) return true;
|
||||
|
||||
if(!comp.equals(gens[x - 1][z])) return true;
|
||||
|
||||
if(!comp.equals(gens[x][z - 1])) return true;
|
||||
|
||||
if(!comp.equals(gens[x + 1][z + 1])) return true;
|
||||
|
||||
if(!comp.equals(gens[x - 1][z - 1])) return true;
|
||||
|
||||
if(!comp.equals(gens[x + 1][z - 1])) return true;
|
||||
|
||||
return !comp.equals(gens[x - 1][z + 1]);
|
||||
private static double noise2dExtrude(double y, double base) {
|
||||
return ((-FastMath.pow2((y / base))) + 1);
|
||||
}
|
||||
|
||||
private double biomeAvg(int x, int y, int z) {
|
||||
if(needsBiomeInterp[x][z]) {
|
||||
double t = 0d;
|
||||
for(int xi = 0; xi <= 2; xi++) {
|
||||
for(int zi = 0; zi <= 2; zi++) {
|
||||
t += noiseStorage[x + xi][z + zi][y];
|
||||
}
|
||||
}
|
||||
return t / 9d;
|
||||
} else {
|
||||
return noiseStorage[x + 1][z + 1][y];
|
||||
}
|
||||
private double biomeAvg(int x, int y, int z, double[][][] noise) {
|
||||
return noise[x][z][y];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,10 +110,6 @@ public class ChunkInterpolator {
|
||||
* @return double - The interpolated noise at the coordinates.
|
||||
*/
|
||||
public double getNoise(double x, double y, double z) {
|
||||
return interpGrid[reRange(((int) x) / smooth, 3)][reRange(((int) y) / 4, 63)][reRange(((int) z) / smooth, 3)].trilerp((x % smooth) / smooth, (y % 4) / 4, (z % smooth) / smooth);
|
||||
}
|
||||
|
||||
private static double noise2dExtrude(double y, double base) {
|
||||
return ((-FastMath.pow2((y / base))) + 1);
|
||||
return interpGrid[reRange(((int) x) / 4, 3)][reRange(((int) y) / 4, 63)][reRange(((int) z) / 4, 3)].trilerp((x % 4) / 4, (y % 4) / 4, (z % 4) / 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.dfsek.terra.bukkit.world.BukkitAdapter;
|
||||
import com.dfsek.terra.bukkit.world.BukkitBiomeGrid;
|
||||
import com.dfsek.terra.config.lang.LangUtil;
|
||||
import com.dfsek.terra.debug.Debug;
|
||||
import com.dfsek.terra.population.CavePopulator;
|
||||
import com.dfsek.terra.population.FloraPopulator;
|
||||
import com.dfsek.terra.population.OrePopulator;
|
||||
import com.dfsek.terra.population.StructurePopulator;
|
||||
@@ -90,7 +89,7 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener
|
||||
|
||||
@Override
|
||||
public @NotNull List<BlockPopulator> getDefaultPopulators(@NotNull World world) {
|
||||
return Stream.of(new CavePopulator(main), new StructurePopulator(main), popMan).map(BukkitPopulatorWrapper::new).collect(Collectors.toList());
|
||||
return Stream.of(new StructurePopulator(main), popMan).map(BukkitPopulatorWrapper::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user