basic noise carver implementation

This commit is contained in:
dfsek
2021-01-24 21:14:21 -07:00
parent bd4812ef2b
commit dca0891e00
13 changed files with 181 additions and 78 deletions

View File

@@ -1,5 +1,8 @@
package com.dfsek.terra.api.math.noise.samplers;
/**
* Sampler implementation that returns a constant.
*/
public class ConstantSampler implements NoiseSampler {
private final double constant;

View File

@@ -1351,6 +1351,8 @@ public class FastNoiseLite implements NoiseSampler {
double distance0 = Double.MAX_VALUE;
double distance1 = Double.MAX_VALUE;
double distance2 = Double.MAX_VALUE;
int closestHash = 0;
double cellularJitter = 0.43701595 * mCellularJitterModifier;
@@ -1382,6 +1384,11 @@ public class FastNoiseLite implements NoiseSampler {
closestHash = hash;
center.setX((xi + RAND_VECS_2D[idx] * cellularJitter) / mFrequency);
center.setZ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / mFrequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
@@ -1407,6 +1414,11 @@ public class FastNoiseLite implements NoiseSampler {
closestHash = hash;
center.setX((xi + RAND_VECS_2D[idx] * cellularJitter) / mFrequency);
center.setZ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / mFrequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
@@ -1432,6 +1444,11 @@ public class FastNoiseLite implements NoiseSampler {
closestHash = hash;
center.setX((xi + RAND_VECS_2D[idx] * cellularJitter) / mFrequency);
center.setZ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / mFrequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
@@ -1464,6 +1481,8 @@ public class FastNoiseLite implements NoiseSampler {
return distance0 / distance1 - 1;
case NoiseLookup:
return cellularNoiseLookup.getNoise(center.getX(), center.getZ());
case Distance3Div:
return distance0 / distance2 - 1;
default:
return 0;
}
@@ -1477,6 +1496,7 @@ public class FastNoiseLite implements NoiseSampler {
double distance0 = Double.MAX_VALUE;
double distance1 = Double.MAX_VALUE;
double distance2 = Double.MAX_VALUE;
int closestHash = 0;
double cellularJitter = 0.39614353 * mCellularJitterModifier;
@@ -1506,13 +1526,17 @@ public class FastNoiseLite implements NoiseSampler {
double newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
center.setX((xi + RAND_VECS_3D[idx] * cellularJitter) / mFrequency);
center.setY((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / mFrequency);
center.setZ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / mFrequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
@@ -1538,13 +1562,17 @@ public class FastNoiseLite implements NoiseSampler {
double newDistance = fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
center.setX((xi + RAND_VECS_3D[idx] * cellularJitter) / mFrequency);
center.setY((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / mFrequency);
center.setZ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / mFrequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
@@ -1578,6 +1606,11 @@ public class FastNoiseLite implements NoiseSampler {
center.setX((xi + RAND_VECS_3D[idx] * cellularJitter) / mFrequency);
center.setY((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / mFrequency);
center.setZ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / mFrequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
@@ -1614,6 +1647,8 @@ public class FastNoiseLite implements NoiseSampler {
return distance0 / distance1 - 1;
case NoiseLookup:
return cellularNoiseLookup.getNoise(center.getX(), center.getY(), center.getZ());
case Distance3Div:
return distance0 / distance2 - 1;
default:
return 0;
}
@@ -2603,7 +2638,8 @@ public class FastNoiseLite implements NoiseSampler {
Distance2Sub,
Distance2Mul,
Distance2Div,
NoiseLookup
NoiseLookup,
Distance3Div
}

View File

@@ -6,16 +6,25 @@ import com.dfsek.terra.api.world.palette.Palette;
public interface Generator {
/**
* Gets the 3D noise at a pair of coordinates using the provided FastNoiseLite instance.
* Gets the noise sampler instance to use for base terrain.
*
* @param x - The x coordinate.
* @param y - The y coordinate.
* @param z - The z coordinate.
* @return double - Noise value at the specified coordinates.
* @return NoiseSampler for terrain
*/
double getNoise(double x, double y, double z);
NoiseSampler getBaseSampler();
double getElevation(int x, int z);
/**
* Gets the noise sampler to use for elevation
*
* @return NoiseSampler for elevation.
*/
NoiseSampler getElevationSampler();
/**
* Gets the noise sampler to use for carving.
*
* @return NoiseSampler for carving.
*/
NoiseSampler getCarver();
int getBlendDistance();

View File

@@ -1,7 +1,8 @@
package com.dfsek.terra.carving;
import com.dfsek.terra.api.platform.world.ChunkAccess;
import com.dfsek.terra.api.platform.world.World;
public interface Carver {
void carve(int chunkX, int chunkZ, ChunkAccess chunk);
void carve(World world, int chunkX, int chunkZ, ChunkAccess chunk);
}

View File

@@ -20,6 +20,8 @@ public class GeneratorBuilder {
private String elevationEquation;
private String carvingEquation;
private Scope varScope;
private Map<String, NoiseBuilder> noiseBuilderMap;
@@ -51,13 +53,15 @@ public class GeneratorBuilder {
return gens.computeIfAbsent(seed, k -> {
NoiseSampler noise;
NoiseSampler elevation;
NoiseSampler carving;
try {
noise = new ExpressionSampler(noiseEquation, varScope, seed, noiseBuilderMap);
elevation = elevationEquation == null ? new ConstantSampler(0) : new ExpressionSampler(elevationEquation, varScope, seed, noiseBuilderMap);
carving = new ExpressionSampler(carvingEquation, varScope, seed, noiseBuilderMap);
} catch(ParseException e) {
throw new RuntimeException(e);
}
return new WorldGenerator(palettes, slantPalettes, noise, elevation, noise2d, base, biomeNoise.build((int) seed), elevationWeight, blendDistance, blendStep, blendWeight);
return new WorldGenerator(palettes, slantPalettes, noise, elevation, carving, noise2d, base, biomeNoise.build((int) seed), elevationWeight, blendDistance, blendStep, blendWeight);
});
}
}
@@ -114,6 +118,10 @@ public class GeneratorBuilder {
this.elevationEquation = elevationEquation;
}
public void setCarvingEquation(String carvingEquation) {
this.carvingEquation = carvingEquation;
}
public Scope getVarScope() {
return varScope;
}

View File

@@ -19,6 +19,7 @@ public class BiomeFactory implements TerraFactory<BiomeTemplate, TerraBiome> {
GeneratorBuilder generatorBuilder = new GeneratorBuilder();
generatorBuilder.setElevationEquation(template.getElevationEquation());
generatorBuilder.setNoiseEquation(template.getNoiseEquation());
generatorBuilder.setCarvingEquation(template.getCarvingEquation());
generatorBuilder.setNoiseBuilderMap(template.getPack().getTemplate().getNoiseBuilderMap());
generatorBuilder.setPalettes(template.getPalette());
generatorBuilder.setSlantPalettes(template.getSlantPalette());

View File

@@ -42,6 +42,11 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
@Default
private String extend = null;
@Value("carving.equation")
@Abstractable
@Default
private String carvingEquation = "0";
@Value("noise-2d.enable")
@Default
@Abstractable
@@ -235,6 +240,10 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
return elevationEquation;
}
public String getCarvingEquation() {
return carvingEquation;
}
public ConfigPack getPack() {
return pack;
}
@@ -317,6 +326,12 @@ public class BiomeTemplate extends AbstractableTemplate implements ValidatedConf
throw new ValidationException("Invalid noise equation: ", e);
}
try {
tester.parse(carvingEquation, testScope);
} catch(ParseException e) {
throw new ValidationException("Invalid carving equation: ", e);
}
try {
if(elevationEquation != null) tester.parse(elevationEquation, testScope);
} catch(ParseException e) {

View File

@@ -1,6 +1,7 @@
package com.dfsek.terra.generation;
import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.block.BlockData;
@@ -19,10 +20,12 @@ import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.biome.BiomeProvider;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.biome.palette.SinglePalette;
import com.dfsek.terra.carving.Carver;
import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.generation.math.Sampler;
import com.dfsek.terra.generation.math.SamplerCache;
import com.dfsek.terra.population.items.carving.NoiseCarver;
import com.dfsek.terra.profiler.ProfileFuture;
import com.dfsek.terra.util.PaletteUtil;
import org.jetbrains.annotations.NotNull;
@@ -38,12 +41,15 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
private final MaterialData water;
private final SinglePalette<BlockData> blank;
private final Carver carver;
private final SamplerCache cache;
public MasterChunkGenerator(ConfigPack c, TerraPlugin main, SamplerCache cache) {
this.configPack = c;
this.main = main;
carver = new NoiseCarver(new Range(0, 255), main.getWorldHandle().createBlockData("minecraft:air"), main);
water = main.getWorldHandle().createMaterialData("minecraft:water");
blank = new SinglePalette<>(main.getWorldHandle().createBlockData("minecraft:air"));
this.cache = cache;
@@ -90,54 +96,55 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
TerraWorld tw = main.getWorld(world);
BiomeProvider grid = tw.getBiomeProvider();
try(ProfileFuture ignore = tw.getProfiler().measure("TotalChunkGenTime")) {
if(!tw.isSafe()) return chunk;
int xOrig = (chunkX << 4);
int zOrig = (chunkZ << 4);
if(!tw.isSafe()) return chunk;
int xOrig = (chunkX << 4);
int zOrig = (chunkZ << 4);
Sampler sampler = cache.getChunk(world, chunkX, chunkZ);
Sampler sampler = cache.getChunk(world, chunkX, chunkZ);
for(byte x = 0; x < 16; x++) {
for(byte z = 0; z < 16; z++) {
int paletteLevel = 0;
for(byte x = 0; x < 16; x++) {
for(byte z = 0; z < 16; z++) {
int paletteLevel = 0;
int cx = xOrig + x;
int cz = zOrig + z;
int cx = xOrig + x;
int cz = zOrig + z;
TerraBiome b = grid.getBiome(xOrig + x, zOrig + z);
BiomeTemplate c = ((UserDefinedBiome) b).getConfig();
TerraBiome b = grid.getBiome(xOrig + x, zOrig + z);
BiomeTemplate c = ((UserDefinedBiome) b).getConfig();
int sea = c.getSeaLevel();
Palette<BlockData> seaPalette = c.getOceanPalette();
int sea = c.getSeaLevel();
Palette<BlockData> seaPalette = c.getOceanPalette();
boolean justSet = false;
BlockData data = null;
for(int y = world.getMaxHeight() - 1; y >= 0; y--) {
if(sampler.sample(x, y, z) > 0) {
justSet = true;
data = PaletteUtil.getPalette(x, y, z, c, sampler).get(paletteLevel, cx, y, cz);
chunk.setBlock(x, y, z, data);
if(paletteLevel == 0 && c.doSlabs() && y < 255) {
prepareBlockPartFloor(data, chunk.getBlockData(x, y + 1, z), chunk, new Vector3(x, y + 1, z), c.getSlabPalettes(),
c.getStairPalettes(), c.getSlabThreshold(), sampler);
}
paletteLevel++;
} else if(y <= sea) {
chunk.setBlock(x, y, z, seaPalette.get(sea - y, x + xOrig, y, z + zOrig));
if(justSet && c.doSlabs()) {
prepareBlockPartCeiling(data, chunk.getBlockData(x, y, z), chunk, new Vector3(x, y, z), c.getSlabPalettes(), c.getStairPalettes(), c.getSlabThreshold(), sampler);
}
justSet = false;
paletteLevel = 0;
} else {
if(justSet && c.doSlabs()) {
prepareBlockPartCeiling(data, chunk.getBlockData(x, y, z), chunk, new Vector3(x, y, z), c.getSlabPalettes(), c.getStairPalettes(), c.getSlabThreshold(), sampler);
}
justSet = false;
paletteLevel = 0;
boolean justSet = false;
BlockData data = null;
for(int y = world.getMaxHeight() - 1; y >= 0; y--) {
if(sampler.sample(x, y, z) > 0) {
justSet = true;
data = PaletteUtil.getPalette(x, y, z, c, sampler).get(paletteLevel, cx, y, cz);
chunk.setBlock(x, y, z, data);
if(paletteLevel == 0 && c.doSlabs() && y < 255) {
prepareBlockPartFloor(data, chunk.getBlockData(x, y + 1, z), chunk, new Vector3(x, y + 1, z), c.getSlabPalettes(),
c.getStairPalettes(), c.getSlabThreshold(), sampler);
}
paletteLevel++;
} else if(y <= sea) {
chunk.setBlock(x, y, z, seaPalette.get(sea - y, x + xOrig, y, z + zOrig));
if(justSet && c.doSlabs()) {
prepareBlockPartCeiling(data, chunk.getBlockData(x, y, z), chunk, new Vector3(x, y, z), c.getSlabPalettes(), c.getStairPalettes(), c.getSlabThreshold(), sampler);
}
justSet = false;
paletteLevel = 0;
} else {
if(justSet && c.doSlabs()) {
prepareBlockPartCeiling(data, chunk.getBlockData(x, y, z), chunk, new Vector3(x, y, z), c.getSlabPalettes(), c.getStairPalettes(), c.getSlabThreshold(), sampler);
}
justSet = false;
paletteLevel = 0;
}
}
}
}
carver.carve(world, chunkX, chunkZ, chunk);
return chunk;
}
}

View File

@@ -14,6 +14,7 @@ public class WorldGenerator implements Generator {
private final NoiseSampler noise;
private final NoiseSampler elevation;
private final NoiseSampler carving;
private final boolean noise2d;
private final double base;
@@ -23,11 +24,12 @@ public class WorldGenerator implements Generator {
private final int blendStep;
private final double blendWeight;
public WorldGenerator(PaletteHolder palettes, PaletteHolder slantPalettes, NoiseSampler noise, NoiseSampler elevation, boolean noise2d, double base, NoiseSampler biomeNoise, double elevationWeight, int blendDistance, int blendStep, double blendWeight) {
public WorldGenerator(PaletteHolder palettes, PaletteHolder slantPalettes, NoiseSampler noise, NoiseSampler elevation, NoiseSampler carving, boolean noise2d, double base, NoiseSampler biomeNoise, double elevationWeight, int blendDistance, int blendStep, double blendWeight) {
this.palettes = palettes;
this.slantPalettes = slantPalettes;
this.noise = noise;
this.elevation = elevation;
this.carving = carving;
this.noise2d = noise2d;
this.base = base;
@@ -39,8 +41,18 @@ public class WorldGenerator implements Generator {
}
@Override
public synchronized double getElevation(int x, int z) {
return elevation.getNoise(x, z);
public NoiseSampler getBaseSampler() {
return noise;
}
@Override
public NoiseSampler getElevationSampler() {
return elevation;
}
@Override
public NoiseSampler getCarver() {
return carving;
}
@Override
@@ -53,11 +65,6 @@ public class WorldGenerator implements Generator {
return blendWeight;
}
@Override
public synchronized double getNoise(double x, double y, double z) {
return noise.getNoise(x, y, z);
}
/**
* Gets the BlockPalette to generate the biome with.
*

View File

@@ -11,11 +11,19 @@ public class Sampler {
private final ElevationInterpolator elevationInterpolator;
public Sampler(int x, int z, BiomeProvider provider, World world, int elevationSmooth) {
this.interpolator = new BiomeChunkInterpolator(world, x, z, provider);
this.interpolator = new BiomeChunkInterpolator(world, x, z, provider, (generator, coord) -> {
if(generator.is2d())
return generator.getBaseSampler().getNoise(coord.getX(), 0, coord.getZ()) + noise2dExtrude(coord.getY(), generator.get2dBase());
else return generator.getBaseSampler().getNoise(coord);
});
this.elevationInterpolator = new ElevationInterpolator(world, x, z, provider, elevationSmooth);
}
public double sample(double x, double y, double z) {
return interpolator.getNoise(x, y, z) + elevationInterpolator.getElevation(FastMath.roundToInt(x), FastMath.roundToInt(z));
}
public static double noise2dExtrude(double y, double base) {
return ((-FastMath.pow2((y / base))) + 1);
}
}

View File

@@ -1,5 +1,6 @@
package com.dfsek.terra.generation.math.interpolation;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.util.mutable.MutableInteger;
import com.dfsek.terra.api.world.biome.Generator;
@@ -8,6 +9,7 @@ import net.jafama.FastMath;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
/**
* Class to abstract away the Interpolators needed to generate a chunk.<br>
@@ -15,6 +17,7 @@ import java.util.Map;
*/
public class BiomeChunkInterpolator implements ChunkInterpolator {
private final Interpolator3[][][] interpGrid = new Interpolator3[4][64][4];
private final BiFunction<Generator, Vector3, Double> noiseGetter;
/**
* Instantiates a 3D BiomeChunkInterpolator at a pair of chunk coordinates.
@@ -23,7 +26,8 @@ public class BiomeChunkInterpolator implements ChunkInterpolator {
* @param chunkZ Z coordinate of the chunk.
* @param provider Biome Provider to use for biome fetching.
*/
public BiomeChunkInterpolator(World w, int chunkX, int chunkZ, BiomeProvider provider) {
public BiomeChunkInterpolator(World w, int chunkX, int chunkZ, BiomeProvider provider, BiFunction<Generator, Vector3, Double> noiseGetter) {
this.noiseGetter = noiseGetter;
int xOrigin = chunkX << 4;
int zOrigin = chunkZ << 4;
@@ -66,7 +70,7 @@ public class BiomeChunkInterpolator implements ChunkInterpolator {
}
}
private static double computeNoise(Map<Generator, MutableInteger> gens, double x, double y, double z) {
private 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()) {
@@ -80,19 +84,14 @@ public class BiomeChunkInterpolator implements ChunkInterpolator {
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 double computeNoise(Generator generator, double x, double y, double z) {
return noiseGetter.apply(generator, new Vector3(x, y, z));
}
private static int reRange(int value, int high) {
return FastMath.max(FastMath.min(value, high), 0);
}
private static double noise2dExtrude(double y, double base) {
return ((-FastMath.pow2((y / base))) + 1);
}
/**
* Gets the noise at a pair of internal chunk coordinates.
*

View File

@@ -28,7 +28,7 @@ public class ElevationInterpolator {
for(int xi = -smooth; xi <= smooth; xi++) {
for(int zi = -smooth; zi <= smooth; zi++) {
Generator gen = gens[x + 1 + smooth + xi][z + 1 + smooth + zi];
noise += gen.getElevation(xOrigin + x, zOrigin + z) * gen.getElevationWeight();
noise += gen.getElevationSampler().getNoise(xOrigin + x, zOrigin + z) * gen.getElevationWeight();
div += gen.getElevationWeight();
}
}

View File

@@ -1,26 +1,35 @@
package com.dfsek.terra.population.items.carving;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.world.ChunkAccess;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.carving.Carver;
import com.dfsek.terra.generation.math.interpolation.NoiseChunkInterpolator;
import com.dfsek.terra.generation.math.interpolation.BiomeChunkInterpolator;
import com.dfsek.terra.generation.math.interpolation.ChunkInterpolator;
public class NoiseCarver implements Carver {
private final NoiseSampler noise;
private final Range range;
private final BlockData data;
private final TerraPlugin main;
public NoiseCarver(NoiseSampler noise, Range range) {
this.noise = noise;
public NoiseCarver(Range range, BlockData data, TerraPlugin main) {
this.range = range;
this.data = data;
this.main = main;
}
@Override
public void carve(int chunkX, int chunkZ, ChunkAccess chunk) {
NoiseChunkInterpolator chunkInterpolator = new NoiseChunkInterpolator(chunkX, chunkZ, noise);
public void carve(World world, int chunkX, int chunkZ, ChunkAccess chunk) {
ChunkInterpolator interpolator = new BiomeChunkInterpolator(world, chunkX, chunkZ, main.getWorld(world).getBiomeProvider(), (gen, coord) -> gen.getCarver().getNoise(coord));
for(int y : range) {
for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) {
double n = interpolator.getNoise(x, y, z);
if(n > 0) chunk.setBlock(x, y, z, data);
}
}
}
}
}