Merge pull request #50 from PolyhedralDev/palette

Noise config additions
This commit is contained in:
dfsek
2021-01-12 10:34:42 -07:00
committed by GitHub
27 changed files with 207 additions and 150 deletions

View File

@@ -4,6 +4,7 @@ import com.dfsek.tectonic.loading.TypeRegistry;
import com.dfsek.terra.api.math.GridSpawn;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.noise.samplers.Normalizer;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.biome.palette.PaletteHolder;
@@ -48,16 +49,17 @@ public class GenericLoaders implements LoaderRegistrar {
.registerLoader(PaletteHolder.class, new PaletteHolderLoader())
.registerLoader(PaletteLayer.class, new PaletteLayerLoader())
.registerLoader(FloraLayer.class, new FloraLayerLoader())
.registerLoader(Ore.Type.class, (t, o, l) -> Ore.Type.valueOf((String) o))
.registerLoader(Ore.Type.class, (t, o, l) -> Ore.Type.valueOf(o.toString()))
.registerLoader(OreConfig.class, new OreConfigLoader())
.registerLoader(NoiseBuilder.class, new NoiseBuilderLoader())
.registerLoader(TreeLayer.class, new TreeLayerLoader())
.registerLoader(MaterialSet.class, new MaterialSetLoader())
.registerLoader(OreHolder.class, new OreHolderLoader())
.registerLoader(ImageLoader.class, new ImageLoaderLoader())
.registerLoader(TerraBiomeGrid.Type.class, (t, o, l) -> TerraBiomeGrid.Type.valueOf((String) o))
.registerLoader(ImageLoader.Channel.class, (t, o, l) -> ImageLoader.Channel.valueOf((String) o))
.registerLoader(ImageLoader.Align.class, (t, o, l) -> ImageLoader.Align.valueOf((String) o))
.registerLoader(TerraFlora.Search.class, (t, o, l) -> TerraFlora.Search.valueOf((String) o));
.registerLoader(TerraBiomeGrid.Type.class, (t, o, l) -> TerraBiomeGrid.Type.valueOf(o.toString()))
.registerLoader(ImageLoader.Channel.class, (t, o, l) -> ImageLoader.Channel.valueOf(o.toString()))
.registerLoader(ImageLoader.Align.class, (t, o, l) -> ImageLoader.Align.valueOf(o.toString()))
.registerLoader(TerraFlora.Search.class, (t, o, l) -> TerraFlora.Search.valueOf(o.toString()))
.registerLoader(Normalizer.NormalType.class, (t, o, l) -> Normalizer.NormalType.valueOf(o.toString().toUpperCase()));
}
}

View File

@@ -1,12 +1,11 @@
package com.dfsek.terra.api.math;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import com.dfsek.terra.api.world.biome.NormalizationUtil;
import net.jafama.FastMath;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
@SuppressWarnings("unchecked")
public class ProbabilityCollection<E> {
@@ -25,19 +24,29 @@ public class ProbabilityCollection<E> {
return this;
}
public E get() {
if(array.length == 0) return null;
return (E) array[ThreadLocalRandom.current().nextInt(array.length)];
}
public E get(Random r) {
if(array.length == 0) return null;
return (E) array[r.nextInt(array.length)];
}
private static double getNoise(double x, double y, double z, NoiseSampler sampler) {
double n = sampler.getNoise(x, y, z);
return FastMath.min(FastMath.max(n, -1), 1);
}
private static double getNoise(double x, double z, NoiseSampler sampler) {
double n = sampler.getNoise(x, z);
return FastMath.min(FastMath.max(n, -1), 1);
}
public E get(NoiseSampler n, double x, double y, double z) {
if(array.length == 0) return null;
return (E) array[FastMath.min(FastMath.floorToInt(((getNoise(x, y, z, n) + 1D) / 2D) * array.length), array.length - 1)];
}
public E get(NoiseSampler n, double x, double z) {
if(array.length == 0) return null;
return (E) array[NormalizationUtil.normalize(n.getNoise(x, z), array.length, 1)];
return (E) array[FastMath.min(FastMath.floorToInt(((getNoise(x, z, n) + 1D) / 2D) * array.length), array.length - 1)];
}
public int getTotalProbability() {

View File

@@ -278,6 +278,7 @@ public class FastNoiseLite implements NoiseSampler {
private static final int PRIME_Y = 1136930381;
private static final int PRIME_Z = 1720413743;
private static final NoiseSampler CELLULAR_LOOKUP_DEFAULT = new FastNoiseLite();
private static final long POSITIVE_POW1 = 0b01111111111L << 52; // Bits that when applied to the exponent/sign section of a double, produce a positive number with a power of 1.
private int mSeed = 1337;
private double mFrequency = 0.01f;
private NoiseType mNoiseType = NoiseType.OpenSimplex2;
@@ -288,7 +289,7 @@ public class FastNoiseLite implements NoiseSampler {
private double mLacunarity = 2.0f;
private double mGain = 0.5f;
private double mWeightedStrength = 0.0f;
private double mPingPongStength = 2.0f;
private double mPingPongStrength = 2.0f;
private double mFractalBounding = 1 / 1.75f;
private CellularDistanceFunction mCellularDistanceFunction = CellularDistanceFunction.EuclideanSq;
private CellularReturnType mCellularReturnType = CellularReturnType.Distance;
@@ -569,7 +570,7 @@ public class FastNoiseLite implements NoiseSampler {
* Default: 2.0
*/
public void setFractalPingPongStrength(double pingPongStrength) {
mPingPongStength = pingPongStrength;
mPingPongStrength = pingPongStrength;
}
/**
@@ -886,7 +887,7 @@ public class FastNoiseLite implements NoiseSampler {
double amp = mFractalBounding;
for(int i = 0; i < mOctaves; i++) {
double noise = pingPong((genNoiseSingle(seed++, x, y) + 1) * mPingPongStength);
double noise = pingPong((genNoiseSingle(seed++, x, y) + 1) * mPingPongStrength);
sum += (noise - 0.5f) * 2 * amp;
amp *= lerp(1.0f, noise, mWeightedStrength);
@@ -905,7 +906,7 @@ public class FastNoiseLite implements NoiseSampler {
double amp = mFractalBounding;
for(int i = 0; i < mOctaves; i++) {
double noise = pingPong((genNoiseSingle(seed++, x, y, z) + 1) * mPingPongStength);
double noise = pingPong((genNoiseSingle(seed++, x, y, z) + 1) * mPingPongStrength);
sum += (noise - 0.5f) * 2 * amp;
amp *= lerp(1.0f, noise, mWeightedStrength);
@@ -1615,25 +1616,32 @@ public class FastNoiseLite implements NoiseSampler {
return lerp(xf0, xf1, ys) * 1.4247691104677813f;
}
private int doubleCast2Int(double f) {
int i = Float.floatToRawIntBits((float) f);
return i ^ (i >> 16);
private static long hash(long in) {
in = (in + 0x7ed55d16) + (in << 12);
in = (in ^ 0xc761c23c) ^ (in >> 19);
in = (in + 0x165667b1) + (in << 5);
in = (in + 0xd3a2646c) ^ (in << 9);
in = (in + 0xfd7046c5) + (in << 3);
in = (in ^ 0xb55a4f09) ^ (in >> 16);
return in;
}
private double singleWhiteNoise(int seed, double x, double y, double z) {
int xi = doubleCast2Int(x);
int yi = doubleCast2Int(y);
int zi = doubleCast2Int(z);
return valCoord(seed, xi, yi, zi);
long hashX = hash(Double.doubleToRawLongBits(x) ^ seed);
long hashZ = hash(Double.doubleToRawLongBits(y) ^ seed);
long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z);
long base = ((hash(hash)) & 0x000fffffffffffffL)
+ POSITIVE_POW1; // Sign and exponent
return (Double.longBitsToDouble(base) - 1.5) * 2;
}
private double singleWhiteNoise(int seed, double x, double y) {
int xi = doubleCast2Int(x);
int yi = doubleCast2Int(y);
return valCoord(seed, xi, yi);
long hashX = hash(Double.doubleToRawLongBits(x) ^ seed);
long hashZ = hash(Double.doubleToRawLongBits(y) ^ seed);
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
long base = (hash(hash) & 0x000fffffffffffffL)
+ POSITIVE_POW1; // Sign and exponent
return (Double.longBitsToDouble(base) - 1.5) * 2;
}
// Simplex/OpenSimplex2 Noise

View File

@@ -0,0 +1,17 @@
package com.dfsek.terra.api.math.noise.samplers;
public class LinearNormalizer extends Normalizer {
private final double min;
private final double max;
public LinearNormalizer(NoiseSampler sampler, double min, double max) {
super(sampler);
this.min = min;
this.max = max;
}
@Override
public double normalize(double in) {
return (in - min) * (2 / (max - min)) - 1;
}
}

View File

@@ -3,7 +3,7 @@ package com.dfsek.terra.api.math.noise.samplers;
public abstract class Normalizer implements NoiseSampler {
private final NoiseSampler sampler;
protected Normalizer(NoiseSampler sampler) {
public Normalizer(NoiseSampler sampler) {
this.sampler = sampler;
}
@@ -18,4 +18,8 @@ public abstract class Normalizer implements NoiseSampler {
public double getNoise(double x, double y, double z) {
return normalize(sampler.getNoise(x, y, z));
}
public enum NormalType {
LINEAR, NONE
}
}

View File

@@ -4,18 +4,20 @@ import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import java.util.List;
public class SimplexPalette<E> extends Palette<E> {
public class NoisePalette<E> extends Palette<E> {
private final NoiseSampler r;
private final boolean is2D;
public SimplexPalette(NoiseSampler r) {
public NoisePalette(NoiseSampler r, boolean is2D) {
this.r = r;
this.is2D = is2D;
}
@Override
public E get(int layer, int x, int z) {
if(layer > this.getSize()) return this.getLayers().get(this.getLayers().size() - 1).get(r, x, z);
public E get(int layer, double x, double y, double z) {
if(layer > this.getSize()) return this.getLayers().get(this.getLayers().size() - 1).get(r, x, y, z, is2D);
List<PaletteLayer<E>> pl = getLayers();
if(layer >= pl.size()) return pl.get(pl.size() - 1).get(r, x, z);
return pl.get(layer).get(r, x, z);
if(layer >= pl.size()) return pl.get(pl.size() - 1).get(r, x, y, z, is2D);
return pl.get(layer).get(r, x, y, z, is2D);
}
}

View File

@@ -55,7 +55,7 @@ public abstract class Palette<E> {
* @param layer - The layer at which to fetch the material.
* @return BlockData - The material fetched.
*/
public abstract E get(int layer, int x, int z);
public abstract E get(int layer, double x, double y, double z);
public int getSize() {
@@ -104,8 +104,9 @@ public abstract class Palette<E> {
return m;
}
public E get(NoiseSampler random, int x, int z) {
if(col) return this.collection.get(random, x, z);
public E get(NoiseSampler random, double x, double y, double z, boolean is2D) {
if(col && is2D) return this.collection.get(random, x, z);
else if(col) return this.collection.get(random, x, y, z);
return m;
}

View File

@@ -11,7 +11,7 @@ public class RandomPalette<E> extends Palette<E> {
}
@Override
public E get(int layer, int x, int z) {
public E get(int layer, double x, double y, double z) {
if(layer > this.getSize()) return this.getLayers().get(this.getLayers().size() - 1).get(r);
List<PaletteLayer<E>> pl = getLayers();
if(layer >= pl.size()) return pl.get(pl.size() - 1).get(r);

View File

@@ -15,28 +15,28 @@ public class TreeGeometry {
}
public static Vector3 getPerpendicular(Vector3 v) {
return v.getZ() < v.getX() ? new Vector3(v.getY(), - v.getX(), 0) : new Vector3(0, - v.getZ(), v.getY());
return v.getZ() < v.getX() ? new Vector3(v.getY(), -v.getX(), 0) : new Vector3(0, -v.getZ(), v.getY());
}
public FractalTree getTree() {
return tree;
}
public void generateSphere(Location l, BlockData m, int radius, boolean overwrite) {
generateSphere(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, overwrite);
public void generateSphere(Location l, BlockData m, int radius, boolean overwrite, Random r) {
generateSphere(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, overwrite, r);
}
public void generateCylinder(Location l, BlockData m, int radius, int height, boolean overwrite) {
generateCylinder(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, height, overwrite);
public void generateCylinder(Location l, BlockData m, int radius, int height, boolean overwrite, Random r) {
generateCylinder(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, height, overwrite, r);
}
public void generateSphere(Location l, ProbabilityCollection<BlockData> m, int radius, boolean overwrite) {
for(int x = - radius; x <= radius; x++) {
for(int y = - radius; y <= radius; y++) {
for(int z = - radius; z <= radius; z++) {
public void generateSphere(Location l, ProbabilityCollection<BlockData> m, int radius, boolean overwrite, Random r) {
for(int x = -radius; x <= radius; x++) {
for(int y = -radius; y <= radius; y++) {
for(int z = -radius; z <= radius; z++) {
Vector3 position = l.toVector().clone().add(new Vector3(x, y, z));
if(l.toVector().distance(position) <= radius + 0.5 && (overwrite || position.toLocation(l.getWorld()).getBlock().isEmpty()))
tree.setBlock(position.toLocation(l.getWorld()), m.get());
tree.setBlock(position.toLocation(l.getWorld()), m.get(r));
}
}
}
@@ -48,19 +48,19 @@ public class TreeGeometry {
for(int z = -radius; z <= radius; z++) {
Vector3 position = l.toVector().clone().add(new Vector3(x, y, z));
if(r.nextInt(100) < sponginess && l.toVector().distance(position) <= radius + 0.5 && (overwrite || position.toLocation(l.getWorld()).getBlock().isEmpty()))
tree.setBlock(position.toLocation(l.getWorld()), m.get());
tree.setBlock(position.toLocation(l.getWorld()), m.get(r));
}
}
}
}
public void generateCylinder(Location l, ProbabilityCollection<BlockData> m, int radius, int height, boolean overwrite) {
for(int x = - radius; x <= radius; x++) {
public void generateCylinder(Location l, ProbabilityCollection<BlockData> m, int radius, int height, boolean overwrite, Random r) {
for(int x = -radius; x <= radius; x++) {
for(int y = 0; y <= height; y++) {
for(int z = - radius; z <= radius; z++) {
for(int z = -radius; z <= radius; z++) {
Vector3 position = l.toVector().clone().add(new Vector3(x, 0, z));
if(l.toVector().distance(position) <= radius + 0.5 && (overwrite || position.toLocation(l.getWorld()).getBlock().isEmpty()))
tree.setBlock(position.toLocation(l.getWorld()), m.get());
tree.setBlock(position.toLocation(l.getWorld()), m.get(r));
}
}
}

View File

@@ -41,13 +41,13 @@ public class OakTree extends FractalTree {
BlockData wood = getMain().getWorldHandle().createBlockData("minecraft:oak_wood");
BlockData leaves = getMain().getWorldHandle().createBlockData("minecraft:oak_leave");
if(recursions > 1) {
geo.generateSphere(l1, leaves, 1 + r.nextInt(2) + (3 - recursions), false);
geo.generateSphere(l1, leaves, 1 + r.nextInt(2) + (3 - recursions), false, r);
if(recursions > 2) return;
}
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), wood, FastMath.max((int) d1, 0), true);
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), wood, FastMath.max((int) d1, 0), true, r);
}
double runningAngle = (double) 45 / (recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.75).rotateAroundX(FastMath.toRadians(runningAngle + getNoise(r))).rotateAroundZ(FastMath.toRadians(getNoise(r))),

View File

@@ -50,13 +50,13 @@ public class ShatteredTree extends FractalTree {
private void growBranch(Location l1, Vector3 diff, double d1, int recursions, Random r) {
if(recursions > 2) {
geo.generateSphere(l1, leaves, 1 + r.nextInt(2), false);
geo.generateSphere(l1, leaves, 1 + r.nextInt(2), false, r);
return;
}
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), bark, FastMath.max((int) d1, 0), true);
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), bark, FastMath.max((int) d1, 0), true, r);
}
double runningAngle = (double) 45 / (recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.9).rotateAroundX(FastMath.toRadians(runningAngle + getNoise(r))).rotateAroundZ(FastMath.toRadians(getNoise(r))),

View File

@@ -50,13 +50,13 @@ public class SmallShatteredTree extends FractalTree {
private void growBranch(Location l1, Vector3 diff, double d1, int recursions, Random r) {
if(recursions > 2) {
geo.generateSphere(l1, leaves, 1 + r.nextInt(2) + (3 - recursions), false);
geo.generateSphere(l1, leaves, 1 + r.nextInt(2) + (3 - recursions), false, r);
return;
}
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), bark, FastMath.max((int) d1, 0), true);
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), bark, FastMath.max((int) d1, 0), true, r);
}
double runningAngle = (double) 45 / (recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.7).rotateAroundX(FastMath.toRadians(runningAngle + getNoise(r))).rotateAroundZ(FastMath.toRadians(getNoise(r))),

View File

@@ -33,19 +33,19 @@ public class SpruceTree extends FractalTree {
*/
@Override
public void grow(Location origin, Random random) {
growTrunk(origin.clone(), new Vector3(0, 16 + random.nextInt(5), 0));
growTrunk(origin.clone(), new Vector3(0, 16 + random.nextInt(5), 0), random);
}
private void growTrunk(Location l1, Vector3 diff) {
private void growTrunk(Location l1, Vector3 diff, Random r) {
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
int rad = 7;
BlockData wood = getMain().getWorldHandle().createBlockData("minecraft:spruce_wood");
BlockData leaves = getMain().getWorldHandle().createBlockData("minecraft:spruce_leave");
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), wood, (int) ((i > d * 0.65) ? 0.5 : 1.5), true);
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), wood, (int) ((i > d * 0.65) ? 0.5 : 1.5), true, r);
if(i > 3)
geo.generateCylinder(l1.clone().add(diff.clone().multiply((double) i / d)), leaves, (int) (((6 - (i % 4))) * (1.25 - ((double) i / d))), 1, false);
geo.generateCylinder(l1.clone().add(diff.clone().multiply((double) i / d)), leaves, (int) (((6 - (i % 4))) * (1.25 - ((double) i / d))), 1, false, r);
}
setBlock(l1.clone().add(diff), leaves);
setBlock(l1.clone().add(diff).add(0, 1, 0), leaves);

View File

@@ -10,7 +10,7 @@ public class SinglePalette<E> extends Palette<E> {
}
@Override
public E get(int i, int i1, int i2) {
public E get(int layer, double x, double y, double z) {
return item;
}
}

View File

@@ -1,25 +1,16 @@
package com.dfsek.terra.config.factories;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.world.palette.NoisePalette;
import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.api.world.palette.RandomPalette;
import com.dfsek.terra.api.world.palette.SimplexPalette;
import com.dfsek.terra.biome.palette.PaletteLayer;
import com.dfsek.terra.config.templates.PaletteTemplate;
public class PaletteFactory implements TerraFactory<PaletteTemplate, Palette<BlockData>> {
@Override
public Palette<BlockData> build(PaletteTemplate config, TerraPlugin main) {
Palette<BlockData> palette;
if(config.isSimplex()) {
FastNoiseLite noise = new FastNoiseLite((int) config.getSeed());
noise.setFrequency(config.getFrequency());
palette = new SimplexPalette<>(noise);
} else palette = new RandomPalette<>(new FastRandom(config.getSeed()));
Palette<BlockData> palette = new NoisePalette<>(config.getNoise().build(2403), config.getNoise().getDimensions() == 2);
for(PaletteLayer layer : config.getPalette()) {
palette.add(layer.getLayer(), layer.getSize());
}

View File

@@ -1,5 +1,6 @@
package com.dfsek.terra.config.loaders.config;
import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.tectonic.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader;
import com.dfsek.tectonic.loading.TypeLoader;
@@ -8,6 +9,7 @@ import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.world.flora.Flora;
import com.dfsek.terra.config.loaders.Types;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.population.items.flora.FloraLayer;
import java.lang.reflect.Type;
@@ -23,12 +25,19 @@ public class FloraLayerLoader implements TypeLoader<FloraLayer> {
if(range == null) throw new LoadException("Flora range unspecified");
ProbabilityCollection<Flora> items = (ProbabilityCollection<Flora>) configLoader.loadType(Types.FLORA_PROBABILITY_COLLECTION_TYPE, map.get("items"));
if(map.containsKey("simplex-frequency")) {
FastNoiseLite noiseLite = new FastNoiseLite();
noiseLite.setFrequency((Double) map.get("simplex-frequency"));
return new FloraLayer(density, range, items, noiseLite);
NoiseBuilder sampler;
if(map.containsKey("distribution")) {
try {
sampler = new NoiseBuilderLoader().load(NoiseBuilder.class, map.get("distribution"), configLoader);
} catch(ConfigException e) {
throw new LoadException("Unable to load noise", e);
}
return new FloraLayer(density, range, items, sampler.build(2403));
}
sampler = new NoiseBuilder();
sampler.setType(FastNoiseLite.NoiseType.WhiteNoise);
sampler.setDimensions(3);
return new FloraLayer(density, range, items, null);
return new FloraLayer(density, range, items, sampler.build(2403));
}
}

View File

@@ -6,6 +6,7 @@ import com.dfsek.tectonic.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader;
import com.dfsek.tectonic.loading.TypeLoader;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.math.noise.samplers.Normalizer;
import com.dfsek.terra.generation.config.NoiseBuilder;
import java.lang.reflect.Type;
@@ -16,12 +17,14 @@ public class NoiseBuilderLoader implements TypeLoader<NoiseBuilder> {
private static final ConfigLoader LOADER = new ConfigLoader();
static {
LOADER.registerLoader(FastNoiseLite.NoiseType.class, (t, object, cf) -> FastNoiseLite.NoiseType.valueOf((String) object));
LOADER.registerLoader(FastNoiseLite.FractalType.class, (t, object, cf) -> FastNoiseLite.FractalType.valueOf((String) object));
LOADER.registerLoader(FastNoiseLite.DomainWarpType.class, (t, object, cf) -> FastNoiseLite.DomainWarpType.valueOf((String) object));
LOADER.registerLoader(FastNoiseLite.RotationType3D.class, (t, object, cf) -> FastNoiseLite.RotationType3D.valueOf((String) object));
LOADER.registerLoader(FastNoiseLite.CellularReturnType.class, (t, object, cf) -> FastNoiseLite.CellularReturnType.valueOf((String) object));
LOADER.registerLoader(FastNoiseLite.CellularDistanceFunction.class, (t, object, cf) -> FastNoiseLite.CellularDistanceFunction.valueOf((String) object));
LOADER.registerLoader(FastNoiseLite.NoiseType.class, (t, object, cf) -> FastNoiseLite.NoiseType.valueOf((String) object))
.registerLoader(FastNoiseLite.FractalType.class, (t, object, cf) -> FastNoiseLite.FractalType.valueOf((String) object))
.registerLoader(FastNoiseLite.DomainWarpType.class, (t, object, cf) -> FastNoiseLite.DomainWarpType.valueOf((String) object))
.registerLoader(FastNoiseLite.RotationType3D.class, (t, object, cf) -> FastNoiseLite.RotationType3D.valueOf((String) object))
.registerLoader(FastNoiseLite.CellularReturnType.class, (t, object, cf) -> FastNoiseLite.CellularReturnType.valueOf((String) object))
.registerLoader(FastNoiseLite.CellularDistanceFunction.class, (t, object, cf) -> FastNoiseLite.CellularDistanceFunction.valueOf((String) object))
.registerLoader(NoiseBuilder.class, new NoiseBuilderLoader())
.registerLoader(Normalizer.NormalType.class, (t, o, l) -> Normalizer.NormalType.valueOf(o.toString().toUpperCase()));
}
@Override

View File

@@ -1,5 +1,7 @@
package com.dfsek.terra.config.loaders.config;
import com.dfsek.tectonic.config.Configuration;
import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.tectonic.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader;
import com.dfsek.tectonic.loading.TypeLoader;
@@ -8,6 +10,7 @@ import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.world.tree.Tree;
import com.dfsek.terra.config.loaders.Types;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.population.items.tree.TreeLayer;
import java.lang.reflect.Type;
@@ -15,10 +18,6 @@ import java.util.Map;
@SuppressWarnings("unchecked")
public class TreeLayerLoader implements TypeLoader<TreeLayer> {
public TreeLayerLoader() {
}
@Override
public TreeLayer load(Type type, Object o, ConfigLoader configLoader) throws LoadException {
Map<String, Object> map = (Map<String, Object>) o;
@@ -27,12 +26,19 @@ public class TreeLayerLoader implements TypeLoader<TreeLayer> {
if(range == null) throw new LoadException("Tree range unspecified");
ProbabilityCollection<Tree> items = (ProbabilityCollection<Tree>) configLoader.loadType(Types.TREE_PROBABILITY_COLLECTION_TYPE, map.get("items"));
if(map.containsKey("simplex-frequency")) {
FastNoiseLite noiseLite = new FastNoiseLite();
noiseLite.setFrequency((Double) map.get("simplex-frequency"));
return new TreeLayer(density, range, items, noiseLite);
NoiseBuilder sampler = new NoiseBuilder();
if(map.containsKey("distribution")) {
try {
configLoader.load(sampler, new Configuration((Map<String, Object>) map.get("distribution")));
} catch(ConfigException e) {
throw new LoadException("Unable to load noise", e);
}
return new TreeLayer(density, range, items, sampler.build(2403));
}
return new TreeLayer(density, range, items, null);
sampler.setType(FastNoiseLite.NoiseType.WhiteNoise);
sampler.setDimensions(3);
return new TreeLayer(density, range, items, sampler.build(2403));
}
}

View File

@@ -3,12 +3,19 @@ package com.dfsek.terra.config.templates;
import com.dfsek.tectonic.annotations.Abstractable;
import com.dfsek.tectonic.annotations.Default;
import com.dfsek.tectonic.annotations.Value;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.biome.palette.PaletteLayer;
import com.dfsek.terra.generation.config.NoiseBuilder;
import java.util.List;
@SuppressWarnings({"FieldMayBeFinal", "unused"})
public class PaletteTemplate extends AbstractableTemplate {
@Value("noise")
@Abstractable
@Default
private NoiseBuilder noise = new NoiseBuilder();
@Value("id")
private String id;
@@ -16,38 +23,20 @@ public class PaletteTemplate extends AbstractableTemplate {
@Abstractable
private List<PaletteLayer> palette;
@Value("simplex")
@Abstractable
@Default
private boolean simplex = false;
@Value("frequency")
@Abstractable
@Default
private double frequency = 0.02D;
@Value("seed")
@Abstractable
@Default
private long seed = 0;
public PaletteTemplate() {
noise.setType(FastNoiseLite.NoiseType.WhiteNoise);
noise.setDimensions(3);
}
public String getID() {
return id;
}
public double getFrequency() {
return frequency;
}
public long getSeed() {
return seed;
}
public List<PaletteLayer> getPalette() {
return palette;
}
public boolean isSimplex() {
return simplex;
public NoiseBuilder getNoise() {
return noise;
}
}

View File

@@ -114,7 +114,7 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
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, cz);
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(),
@@ -122,7 +122,7 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
}
paletteLevel++;
} else if(y <= sea) {
chunk.setBlock(x, y, z, seaPalette.get(sea - y, x + xOrig, z + zOrig));
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);
}
@@ -144,18 +144,18 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
private void prepareBlockPartFloor(BlockData down, BlockData orig, ChunkGenerator.ChunkData chunk, Vector3 block, Map<MaterialData, Palette<BlockData>> slabs,
Map<MaterialData, Palette<BlockData>> stairs, double thresh, Sampler sampler) {
if(sampler.sample(block.getBlockX(), block.getBlockY() - 0.4, block.getBlockZ()) > thresh) {
if(sampler.sample(block.getX(), block.getY() - 0.4, block.getZ()) > thresh) {
if(stairs != null) {
Palette<BlockData> stairPalette = stairs.get(down.getMaterial());
if(stairPalette != null) {
BlockData stair = stairPalette.get(0, block.getBlockX(), block.getBlockZ()).clone();
BlockData stair = stairPalette.get(0, block.getX(), block.getY(), block.getZ()).clone();
if(stair instanceof Stairs) {
Stairs stairNew = (Stairs) stair;
if(placeStair(orig, chunk, block, thresh, sampler, stairNew)) return; // Successfully placed part.
}
}
}
BlockData slab = slabs.getOrDefault(down.getMaterial(), blank).get(0, block.getBlockX(), block.getBlockZ());
BlockData slab = slabs.getOrDefault(down.getMaterial(), blank).get(0, block.getX(), block.getY(), block.getZ());
if(slab instanceof Waterlogged) {
((Waterlogged) slab).setWaterlogged(orig.matches(water));
} else if(orig.matches(water)) return;
@@ -165,11 +165,11 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
private void prepareBlockPartCeiling(BlockData up, BlockData orig, ChunkGenerator.ChunkData chunk, Vector3 block, Map<MaterialData, Palette<BlockData>> slabs,
Map<MaterialData, Palette<BlockData>> stairs, double thresh, Sampler sampler) {
if(sampler.sample(block.getBlockX(), block.getBlockY() + 0.4, block.getBlockZ()) > thresh) {
if(sampler.sample(block.getX(), block.getY() + 0.4, block.getZ()) > thresh) {
if(stairs != null) {
Palette<BlockData> stairPalette = stairs.get(up.getMaterial());
if(stairPalette != null) {
BlockData stair = stairPalette.get(0, block.getBlockX(), block.getBlockZ()).clone();
BlockData stair = stairPalette.get(0, block.getX(), block.getY(), block.getZ()).clone();
if(stair instanceof Stairs) {
Stairs stairNew = (Stairs) stair.clone();
stairNew.setHalf(Bisected.Half.TOP);
@@ -177,7 +177,7 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
}
}
}
BlockData slab = slabs.getOrDefault(up.getMaterial(), blank).get(0, block.getBlockX(), block.getBlockZ()).clone();
BlockData slab = slabs.getOrDefault(up.getMaterial(), blank).get(0, block.getX(), block.getY(), block.getZ()).clone();
if(slab instanceof Bisected) ((Bisected) slab).setHalf(Bisected.Half.TOP);
if(slab instanceof Slab) ((Slab) slab).setType(Slab.Type.TOP);
if(slab instanceof Waterlogged) {
@@ -189,14 +189,14 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
private boolean placeStair(BlockData orig, ChunkGenerator.ChunkData chunk, Vector3 block, double thresh, Sampler sampler, Stairs stairNew) {
if(sampler.sample(block.getBlockX() - 0.55, block.getBlockY(), block.getBlockZ()) > thresh) {
if(sampler.sample(block.getBlockX() - 0.55, block.getY(), block.getZ()) > thresh) {
stairNew.setFacing(BlockFace.WEST);
} else if(sampler.sample(block.getBlockX(), block.getBlockY(), block.getBlockZ() - 0.55) > thresh) {
} else if(sampler.sample(block.getBlockX(), block.getY(), block.getZ() - 0.55) > thresh) {
stairNew.setFacing(BlockFace.NORTH);
} else if(sampler.sample(block.getBlockX(), block.getBlockY(), block.getBlockZ() + 0.55) > thresh) {
} else if(sampler.sample(block.getBlockX(), block.getY(), block.getZ() + 0.55) > thresh) {
stairNew.setFacing(BlockFace.SOUTH);
} else if(sampler.sample(block.getBlockX() + 0.55, block.getBlockY(), block.getBlockZ()) > thresh) {
} else if(sampler.sample(block.getX() + 0.55, block.getY(), block.getZ()) > thresh) {
stairNew.setFacing(BlockFace.EAST);
} else stairNew = null;
if(stairNew != null) {

View File

@@ -4,8 +4,11 @@ import com.dfsek.tectonic.annotations.Default;
import com.dfsek.tectonic.annotations.Value;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.math.noise.samplers.LinearNormalizer;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import com.dfsek.terra.api.math.noise.samplers.Normalizer;
@SuppressWarnings("FieldMayBeFinal")
public class NoiseBuilder implements ConfigTemplate {
@Value("type")
@Default
@@ -39,7 +42,7 @@ public class NoiseBuilder implements ConfigTemplate {
@Default
private double weightedStrength = 0.0D;
@Value("offset")
@Value("salt")
@Default
private int seedOffset = 0;
@@ -71,6 +74,22 @@ public class NoiseBuilder implements ConfigTemplate {
@Default
private int dimensions = 2;
@Value("cellular.lookup")
@Default
private NoiseBuilder lookup;
@Value("normalize.type")
@Default
private Normalizer.NormalType normalType = Normalizer.NormalType.NONE;
@Value("normalize.linear.min")
@Default
private double linearMin = -1D;
@Value("normalize.linear.max")
@Default
private double linearMax = 1D;
public NoiseSampler build(int seed) {
FastNoiseLite noise = new FastNoiseLite(seed + seedOffset);
if(!fractalType.equals(FastNoiseLite.FractalType.None)) {
@@ -85,6 +104,7 @@ public class NoiseBuilder implements ConfigTemplate {
noise.setCellularDistanceFunction(cellularDistanceFunction);
noise.setCellularReturnType(cellularReturnType);
noise.setCellularJitter(cellularJitter);
if(lookup != null) noise.setCellularNoiseLookup(lookup.build(seed));
}
noise.setNoiseType(type);
@@ -95,6 +115,7 @@ public class NoiseBuilder implements ConfigTemplate {
noise.setRotationType3D(rotationType3D);
noise.setFrequency(frequency);
if(!normalType.equals(Normalizer.NormalType.NONE)) return new LinearNormalizer(noise, linearMin, linearMax);
return noise;
}

View File

@@ -55,7 +55,7 @@ public class FloraPopulator implements TerraBlockPopulator {
if(entry.getValue().size() <= iter) continue;
finished = false;
FloraLayer layer = entry.getValue().get(iter);
if(layer.getDensity() >= random.nextDouble() * 100D) layer.place(chunk, entry.getKey(), random);
if(layer.getDensity() >= random.nextDouble() * 100D) layer.place(chunk, entry.getKey());
}
iter++;
}

View File

@@ -42,7 +42,7 @@ public class TreePopulator implements TerraBlockPopulator {
UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z, GenerationPhase.POPULATE);
for(TreeLayer layer : biome.getConfig().getTrees()) {
if(layer.getDensity() >= random.nextDouble() * 100)
layer.place(chunk, new Vector2(offset(random, x), offset(random, z)), random);
layer.place(chunk, new Vector2(offset(random, x), offset(random, z)));
}
}
}

View File

@@ -6,8 +6,6 @@ import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import com.dfsek.terra.api.math.vector.Vector2;
import com.dfsek.terra.api.platform.world.Chunk;
import java.util.Random;
public abstract class PlaceableLayer<T> {
protected final double density;
protected final Range level;
@@ -37,5 +35,5 @@ public abstract class PlaceableLayer<T> {
return layer;
}
public abstract void place(Chunk chunk, Vector2 coords, Random random);
public abstract void place(Chunk chunk, Vector2 coords);
}

View File

@@ -8,8 +8,6 @@ import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.world.flora.Flora;
import com.dfsek.terra.population.items.PlaceableLayer;
import java.util.Random;
public class FloraLayer extends PlaceableLayer<Flora> {
public FloraLayer(double density, Range level, ProbabilityCollection<Flora> layer, NoiseSampler noise) {
@@ -21,8 +19,8 @@ public class FloraLayer extends PlaceableLayer<Flora> {
}
@Override
public void place(Chunk chunk, Vector2 coords, Random random) {
Flora item = noise == null ? layer.get(random) : layer.get(noise, (chunk.getX() << 4) + coords.getX(), (chunk.getZ() << 4) + coords.getZ());
public void place(Chunk chunk, Vector2 coords) {
Flora item = layer.get(noise, (chunk.getX() << 4) + coords.getX(), (chunk.getZ() << 4) + coords.getZ());
item.getValidSpawnsAt(chunk, (int) coords.getX(), (int) coords.getZ(), level).forEach(block -> item.plant(block.getLocation()));
}
}

View File

@@ -106,7 +106,7 @@ public class TerraFlora implements Flora {
for(int i = 0; FastMath.abs(i) < size; i += c) { // Down if ceiling, up if floor
int lvl = (FastMath.abs(i));
BlockData data = floraPalette.get((ceiling ? lvl : size - lvl - 1), location.getBlockX(), location.getBlockZ()).clone();
BlockData data = floraPalette.get((ceiling ? lvl : size - lvl - 1), location.getX(), location.getY(), location.getZ()).clone();
if(doRotation) {
if(data instanceof Directional) {
((Directional) data).setFacing(oneFace);

View File

@@ -9,8 +9,7 @@ import com.dfsek.terra.api.platform.block.BlockFace;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.world.tree.Tree;
import com.dfsek.terra.population.items.PlaceableLayer;
import java.util.Random;
import com.dfsek.terra.util.PopulationUtil;
public class TreeLayer extends PlaceableLayer<Tree> {
@@ -19,13 +18,13 @@ public class TreeLayer extends PlaceableLayer<Tree> {
}
@Override
public void place(Chunk chunk, Vector2 coords, Random random) {
Tree item = layer.get(random);
public void place(Chunk chunk, Vector2 coords) {
Tree item = layer.get(noise, coords.getX(), coords.getZ());
Block current = chunk.getBlock((int) coords.getX(), level.getMax(), (int) coords.getZ());
for(int ignored : level) {
current = current.getRelative(BlockFace.DOWN);
if(item.getSpawnable().contains(current.getType())) {
item.plant(current.getLocation().add(0, 1, 0), random);
item.plant(current.getLocation().add(0, 1, 0), PopulationUtil.getRandom(chunk));
}
}
}