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
@@ -4,6 +4,7 @@ import com.dfsek.tectonic.loading.TypeRegistry;
import com.dfsek.terra.api.math.GridSpawn; import com.dfsek.terra.api.math.GridSpawn;
import com.dfsek.terra.api.math.ProbabilityCollection; import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.Range; 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.api.platform.TerraPlugin;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid; import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.biome.palette.PaletteHolder; import com.dfsek.terra.biome.palette.PaletteHolder;
@@ -48,16 +49,17 @@ public class GenericLoaders implements LoaderRegistrar {
.registerLoader(PaletteHolder.class, new PaletteHolderLoader()) .registerLoader(PaletteHolder.class, new PaletteHolderLoader())
.registerLoader(PaletteLayer.class, new PaletteLayerLoader()) .registerLoader(PaletteLayer.class, new PaletteLayerLoader())
.registerLoader(FloraLayer.class, new FloraLayerLoader()) .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(OreConfig.class, new OreConfigLoader())
.registerLoader(NoiseBuilder.class, new NoiseBuilderLoader()) .registerLoader(NoiseBuilder.class, new NoiseBuilderLoader())
.registerLoader(TreeLayer.class, new TreeLayerLoader()) .registerLoader(TreeLayer.class, new TreeLayerLoader())
.registerLoader(MaterialSet.class, new MaterialSetLoader()) .registerLoader(MaterialSet.class, new MaterialSetLoader())
.registerLoader(OreHolder.class, new OreHolderLoader()) .registerLoader(OreHolder.class, new OreHolderLoader())
.registerLoader(ImageLoader.class, new ImageLoaderLoader()) .registerLoader(ImageLoader.class, new ImageLoaderLoader())
.registerLoader(TerraBiomeGrid.Type.class, (t, o, l) -> TerraBiomeGrid.Type.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((String) o)) .registerLoader(ImageLoader.Channel.class, (t, o, l) -> ImageLoader.Channel.valueOf(o.toString()))
.registerLoader(ImageLoader.Align.class, (t, o, l) -> ImageLoader.Align.valueOf((String) o)) .registerLoader(ImageLoader.Align.class, (t, o, l) -> ImageLoader.Align.valueOf(o.toString()))
.registerLoader(TerraFlora.Search.class, (t, o, l) -> TerraFlora.Search.valueOf((String) o)); .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()));
} }
} }
@@ -1,12 +1,11 @@
package com.dfsek.terra.api.math; package com.dfsek.terra.api.math;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler; 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.HashSet;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class ProbabilityCollection<E> { public class ProbabilityCollection<E> {
@@ -25,19 +24,29 @@ public class ProbabilityCollection<E> {
return this; return this;
} }
public E get() {
if(array.length == 0) return null;
return (E) array[ThreadLocalRandom.current().nextInt(array.length)];
}
public E get(Random r) { public E get(Random r) {
if(array.length == 0) return null; if(array.length == 0) return null;
return (E) array[r.nextInt(array.length)]; 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) { public E get(NoiseSampler n, double x, double z) {
if(array.length == 0) return null; 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() { public int getTotalProbability() {
@@ -278,6 +278,7 @@ public class FastNoiseLite implements NoiseSampler {
private static final int PRIME_Y = 1136930381; private static final int PRIME_Y = 1136930381;
private static final int PRIME_Z = 1720413743; private static final int PRIME_Z = 1720413743;
private static final NoiseSampler CELLULAR_LOOKUP_DEFAULT = new FastNoiseLite(); 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 int mSeed = 1337;
private double mFrequency = 0.01f; private double mFrequency = 0.01f;
private NoiseType mNoiseType = NoiseType.OpenSimplex2; private NoiseType mNoiseType = NoiseType.OpenSimplex2;
@@ -288,7 +289,7 @@ public class FastNoiseLite implements NoiseSampler {
private double mLacunarity = 2.0f; private double mLacunarity = 2.0f;
private double mGain = 0.5f; private double mGain = 0.5f;
private double mWeightedStrength = 0.0f; private double mWeightedStrength = 0.0f;
private double mPingPongStength = 2.0f; private double mPingPongStrength = 2.0f;
private double mFractalBounding = 1 / 1.75f; private double mFractalBounding = 1 / 1.75f;
private CellularDistanceFunction mCellularDistanceFunction = CellularDistanceFunction.EuclideanSq; private CellularDistanceFunction mCellularDistanceFunction = CellularDistanceFunction.EuclideanSq;
private CellularReturnType mCellularReturnType = CellularReturnType.Distance; private CellularReturnType mCellularReturnType = CellularReturnType.Distance;
@@ -569,7 +570,7 @@ public class FastNoiseLite implements NoiseSampler {
* Default: 2.0 * Default: 2.0
*/ */
public void setFractalPingPongStrength(double pingPongStrength) { public void setFractalPingPongStrength(double pingPongStrength) {
mPingPongStength = pingPongStrength; mPingPongStrength = pingPongStrength;
} }
/** /**
@@ -886,7 +887,7 @@ public class FastNoiseLite implements NoiseSampler {
double amp = mFractalBounding; double amp = mFractalBounding;
for(int i = 0; i < mOctaves; i++) { 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; sum += (noise - 0.5f) * 2 * amp;
amp *= lerp(1.0f, noise, mWeightedStrength); amp *= lerp(1.0f, noise, mWeightedStrength);
@@ -905,7 +906,7 @@ public class FastNoiseLite implements NoiseSampler {
double amp = mFractalBounding; double amp = mFractalBounding;
for(int i = 0; i < mOctaves; i++) { 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; sum += (noise - 0.5f) * 2 * amp;
amp *= lerp(1.0f, noise, mWeightedStrength); amp *= lerp(1.0f, noise, mWeightedStrength);
@@ -1615,25 +1616,32 @@ public class FastNoiseLite implements NoiseSampler {
return lerp(xf0, xf1, ys) * 1.4247691104677813f; return lerp(xf0, xf1, ys) * 1.4247691104677813f;
} }
private int doubleCast2Int(double f) { private static long hash(long in) {
int i = Float.floatToRawIntBits((float) f); in = (in + 0x7ed55d16) + (in << 12);
in = (in ^ 0xc761c23c) ^ (in >> 19);
return i ^ (i >> 16); 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) { private double singleWhiteNoise(int seed, double x, double y, double z) {
int xi = doubleCast2Int(x); long hashX = hash(Double.doubleToRawLongBits(x) ^ seed);
int yi = doubleCast2Int(y); long hashZ = hash(Double.doubleToRawLongBits(y) ^ seed);
int zi = doubleCast2Int(z); long hash = (((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed) + Double.doubleToRawLongBits(z);
long base = ((hash(hash)) & 0x000fffffffffffffL)
return valCoord(seed, xi, yi, zi); + POSITIVE_POW1; // Sign and exponent
return (Double.longBitsToDouble(base) - 1.5) * 2;
} }
private double singleWhiteNoise(int seed, double x, double y) { private double singleWhiteNoise(int seed, double x, double y) {
int xi = doubleCast2Int(x); long hashX = hash(Double.doubleToRawLongBits(x) ^ seed);
int yi = doubleCast2Int(y); long hashZ = hash(Double.doubleToRawLongBits(y) ^ seed);
long hash = ((hashX ^ (hashX >>> 32)) + ((hashZ ^ (hashZ >>> 32)) << 32)) ^ seed;
return valCoord(seed, xi, yi); long base = (hash(hash) & 0x000fffffffffffffL)
+ POSITIVE_POW1; // Sign and exponent
return (Double.longBitsToDouble(base) - 1.5) * 2;
} }
// Simplex/OpenSimplex2 Noise // Simplex/OpenSimplex2 Noise
@@ -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;
}
}
@@ -3,7 +3,7 @@ package com.dfsek.terra.api.math.noise.samplers;
public abstract class Normalizer implements NoiseSampler { public abstract class Normalizer implements NoiseSampler {
private final NoiseSampler sampler; private final NoiseSampler sampler;
protected Normalizer(NoiseSampler sampler) { public Normalizer(NoiseSampler sampler) {
this.sampler = sampler; this.sampler = sampler;
} }
@@ -18,4 +18,8 @@ public abstract class Normalizer implements NoiseSampler {
public double getNoise(double x, double y, double z) { public double getNoise(double x, double y, double z) {
return normalize(sampler.getNoise(x, y, z)); return normalize(sampler.getNoise(x, y, z));
} }
public enum NormalType {
LINEAR, NONE
}
} }
@@ -4,18 +4,20 @@ import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
import java.util.List; import java.util.List;
public class SimplexPalette<E> extends Palette<E> { public class NoisePalette<E> extends Palette<E> {
private final NoiseSampler r; private final NoiseSampler r;
private final boolean is2D;
public SimplexPalette(NoiseSampler r) { public NoisePalette(NoiseSampler r, boolean is2D) {
this.r = r; this.r = r;
this.is2D = is2D;
} }
@Override @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, x, z); if(layer > this.getSize()) return this.getLayers().get(this.getLayers().size() - 1).get(r, x, y, z, is2D);
List<PaletteLayer<E>> pl = getLayers(); List<PaletteLayer<E>> pl = getLayers();
if(layer >= pl.size()) return pl.get(pl.size() - 1).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, z); return pl.get(layer).get(r, x, y, z, is2D);
} }
} }
@@ -55,7 +55,7 @@ public abstract class Palette<E> {
* @param layer - The layer at which to fetch the material. * @param layer - The layer at which to fetch the material.
* @return BlockData - The material fetched. * @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() { public int getSize() {
@@ -104,8 +104,9 @@ public abstract class Palette<E> {
return m; return m;
} }
public E get(NoiseSampler random, int x, int z) { public E get(NoiseSampler random, double x, double y, double z, boolean is2D) {
if(col) return this.collection.get(random, x, z); if(col && is2D) return this.collection.get(random, x, z);
else if(col) return this.collection.get(random, x, y, z);
return m; return m;
} }
@@ -11,7 +11,7 @@ public class RandomPalette<E> extends Palette<E> {
} }
@Override @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); if(layer > this.getSize()) return this.getLayers().get(this.getLayers().size() - 1).get(r);
List<PaletteLayer<E>> pl = getLayers(); List<PaletteLayer<E>> pl = getLayers();
if(layer >= pl.size()) return pl.get(pl.size() - 1).get(r); if(layer >= pl.size()) return pl.get(pl.size() - 1).get(r);
@@ -22,21 +22,21 @@ public class TreeGeometry {
return tree; return tree;
} }
public void generateSphere(Location l, BlockData m, int radius, boolean overwrite) { public void generateSphere(Location l, BlockData m, int radius, boolean overwrite, Random r) {
generateSphere(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, overwrite); generateSphere(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, overwrite, r);
} }
public void generateCylinder(Location l, BlockData m, int radius, int height, boolean 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); generateCylinder(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, height, overwrite, r);
} }
public void generateSphere(Location l, ProbabilityCollection<BlockData> m, int radius, boolean overwrite) { public void generateSphere(Location l, ProbabilityCollection<BlockData> m, int radius, boolean overwrite, Random r) {
for(int x = -radius; x <= radius; x++) { for(int x = -radius; x <= radius; x++) {
for(int y = -radius; y <= radius; y++) { for(int y = -radius; y <= radius; y++) {
for(int z = -radius; z <= radius; z++) { for(int z = -radius; z <= radius; z++) {
Vector3 position = l.toVector().clone().add(new Vector3(x, y, 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())) 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++) { for(int z = -radius; z <= radius; z++) {
Vector3 position = l.toVector().clone().add(new Vector3(x, y, 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())) 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) { 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 x = -radius; x <= radius; x++) {
for(int y = 0; y <= height; y++) { 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)); 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())) 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));
} }
} }
} }
@@ -41,13 +41,13 @@ public class OakTree extends FractalTree {
BlockData wood = getMain().getWorldHandle().createBlockData("minecraft:oak_wood"); BlockData wood = getMain().getWorldHandle().createBlockData("minecraft:oak_wood");
BlockData leaves = getMain().getWorldHandle().createBlockData("minecraft:oak_leave"); BlockData leaves = getMain().getWorldHandle().createBlockData("minecraft:oak_leave");
if(recursions > 1) { 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(recursions > 2) return;
} }
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI); if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length(); int d = (int) diff.length();
for(int i = 0; i < d; i++) { 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); 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))), growBranch(l1.clone().add(diff), diff.clone().multiply(0.75).rotateAroundX(FastMath.toRadians(runningAngle + getNoise(r))).rotateAroundZ(FastMath.toRadians(getNoise(r))),
@@ -50,13 +50,13 @@ public class ShatteredTree extends FractalTree {
private void growBranch(Location l1, Vector3 diff, double d1, int recursions, Random r) { private void growBranch(Location l1, Vector3 diff, double d1, int recursions, Random r) {
if(recursions > 2) { if(recursions > 2) {
geo.generateSphere(l1, leaves, 1 + r.nextInt(2), false); geo.generateSphere(l1, leaves, 1 + r.nextInt(2), false, r);
return; return;
} }
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI); if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length(); int d = (int) diff.length();
for(int i = 0; i < d; i++) { 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); 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))), growBranch(l1.clone().add(diff), diff.clone().multiply(0.9).rotateAroundX(FastMath.toRadians(runningAngle + getNoise(r))).rotateAroundZ(FastMath.toRadians(getNoise(r))),
@@ -50,13 +50,13 @@ public class SmallShatteredTree extends FractalTree {
private void growBranch(Location l1, Vector3 diff, double d1, int recursions, Random r) { private void growBranch(Location l1, Vector3 diff, double d1, int recursions, Random r) {
if(recursions > 2) { 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; return;
} }
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI); if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length(); int d = (int) diff.length();
for(int i = 0; i < d; i++) { 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); 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))), growBranch(l1.clone().add(diff), diff.clone().multiply(0.7).rotateAroundX(FastMath.toRadians(runningAngle + getNoise(r))).rotateAroundZ(FastMath.toRadians(getNoise(r))),
@@ -33,19 +33,19 @@ public class SpruceTree extends FractalTree {
*/ */
@Override @Override
public void grow(Location origin, Random random) { 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); if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length(); int d = (int) diff.length();
int rad = 7; int rad = 7;
BlockData wood = getMain().getWorldHandle().createBlockData("minecraft:spruce_wood"); BlockData wood = getMain().getWorldHandle().createBlockData("minecraft:spruce_wood");
BlockData leaves = getMain().getWorldHandle().createBlockData("minecraft:spruce_leave"); BlockData leaves = getMain().getWorldHandle().createBlockData("minecraft:spruce_leave");
for(int i = 0; i < d; i++) { 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) 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), leaves);
setBlock(l1.clone().add(diff).add(0, 1, 0), leaves); setBlock(l1.clone().add(diff).add(0, 1, 0), leaves);
@@ -10,7 +10,7 @@ public class SinglePalette<E> extends Palette<E> {
} }
@Override @Override
public E get(int i, int i1, int i2) { public E get(int layer, double x, double y, double z) {
return item; return item;
} }
} }
@@ -1,25 +1,16 @@
package com.dfsek.terra.config.factories; 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.TerraPlugin;
import com.dfsek.terra.api.platform.block.BlockData; 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.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.biome.palette.PaletteLayer;
import com.dfsek.terra.config.templates.PaletteTemplate; import com.dfsek.terra.config.templates.PaletteTemplate;
public class PaletteFactory implements TerraFactory<PaletteTemplate, Palette<BlockData>> { public class PaletteFactory implements TerraFactory<PaletteTemplate, Palette<BlockData>> {
@Override @Override
public Palette<BlockData> build(PaletteTemplate config, TerraPlugin main) { public Palette<BlockData> build(PaletteTemplate config, TerraPlugin main) {
Palette<BlockData> palette; Palette<BlockData> palette = new NoisePalette<>(config.getNoise().build(2403), config.getNoise().getDimensions() == 2);
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()));
for(PaletteLayer layer : config.getPalette()) { for(PaletteLayer layer : config.getPalette()) {
palette.add(layer.getLayer(), layer.getSize()); palette.add(layer.getLayer(), layer.getSize());
} }
@@ -1,5 +1,6 @@
package com.dfsek.terra.config.loaders.config; package com.dfsek.terra.config.loaders.config;
import com.dfsek.tectonic.exception.ConfigException;
import com.dfsek.tectonic.exception.LoadException; import com.dfsek.tectonic.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader; import com.dfsek.tectonic.loading.ConfigLoader;
import com.dfsek.tectonic.loading.TypeLoader; 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.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.world.flora.Flora; import com.dfsek.terra.api.world.flora.Flora;
import com.dfsek.terra.config.loaders.Types; import com.dfsek.terra.config.loaders.Types;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.population.items.flora.FloraLayer; import com.dfsek.terra.population.items.flora.FloraLayer;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@@ -23,12 +25,19 @@ public class FloraLayerLoader implements TypeLoader<FloraLayer> {
if(range == null) throw new LoadException("Flora range unspecified"); if(range == null) throw new LoadException("Flora range unspecified");
ProbabilityCollection<Flora> items = (ProbabilityCollection<Flora>) configLoader.loadType(Types.FLORA_PROBABILITY_COLLECTION_TYPE, map.get("items")); ProbabilityCollection<Flora> items = (ProbabilityCollection<Flora>) configLoader.loadType(Types.FLORA_PROBABILITY_COLLECTION_TYPE, map.get("items"));
if(map.containsKey("simplex-frequency")) { NoiseBuilder sampler;
FastNoiseLite noiseLite = new FastNoiseLite(); if(map.containsKey("distribution")) {
noiseLite.setFrequency((Double) map.get("simplex-frequency")); try {
return new FloraLayer(density, range, items, noiseLite); 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));
} }
} }
@@ -6,6 +6,7 @@ import com.dfsek.tectonic.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader; import com.dfsek.tectonic.loading.ConfigLoader;
import com.dfsek.tectonic.loading.TypeLoader; import com.dfsek.tectonic.loading.TypeLoader;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite; 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 com.dfsek.terra.generation.config.NoiseBuilder;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@@ -16,12 +17,14 @@ public class NoiseBuilderLoader implements TypeLoader<NoiseBuilder> {
private static final ConfigLoader LOADER = new ConfigLoader(); private static final ConfigLoader LOADER = new ConfigLoader();
static { static {
LOADER.registerLoader(FastNoiseLite.NoiseType.class, (t, object, cf) -> FastNoiseLite.NoiseType.valueOf((String) object)); 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)); .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)); .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)); .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)); .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)); .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 @Override
@@ -1,5 +1,7 @@
package com.dfsek.terra.config.loaders.config; 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.exception.LoadException;
import com.dfsek.tectonic.loading.ConfigLoader; import com.dfsek.tectonic.loading.ConfigLoader;
import com.dfsek.tectonic.loading.TypeLoader; 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.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.world.tree.Tree; import com.dfsek.terra.api.world.tree.Tree;
import com.dfsek.terra.config.loaders.Types; import com.dfsek.terra.config.loaders.Types;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.population.items.tree.TreeLayer; import com.dfsek.terra.population.items.tree.TreeLayer;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@@ -15,10 +18,6 @@ import java.util.Map;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class TreeLayerLoader implements TypeLoader<TreeLayer> { public class TreeLayerLoader implements TypeLoader<TreeLayer> {
public TreeLayerLoader() {
}
@Override @Override
public TreeLayer load(Type type, Object o, ConfigLoader configLoader) throws LoadException { public TreeLayer load(Type type, Object o, ConfigLoader configLoader) throws LoadException {
Map<String, Object> map = (Map<String, Object>) o; 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"); if(range == null) throw new LoadException("Tree range unspecified");
ProbabilityCollection<Tree> items = (ProbabilityCollection<Tree>) configLoader.loadType(Types.TREE_PROBABILITY_COLLECTION_TYPE, map.get("items")); ProbabilityCollection<Tree> items = (ProbabilityCollection<Tree>) configLoader.loadType(Types.TREE_PROBABILITY_COLLECTION_TYPE, map.get("items"));
if(map.containsKey("simplex-frequency")) { NoiseBuilder sampler = new NoiseBuilder();
FastNoiseLite noiseLite = new FastNoiseLite(); if(map.containsKey("distribution")) {
noiseLite.setFrequency((Double) map.get("simplex-frequency")); try {
return new TreeLayer(density, range, items, noiseLite); 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));
} }
} }
@@ -3,12 +3,19 @@ package com.dfsek.terra.config.templates;
import com.dfsek.tectonic.annotations.Abstractable; import com.dfsek.tectonic.annotations.Abstractable;
import com.dfsek.tectonic.annotations.Default; import com.dfsek.tectonic.annotations.Default;
import com.dfsek.tectonic.annotations.Value; 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.biome.palette.PaletteLayer;
import com.dfsek.terra.generation.config.NoiseBuilder;
import java.util.List; import java.util.List;
@SuppressWarnings({"FieldMayBeFinal", "unused"}) @SuppressWarnings({"FieldMayBeFinal", "unused"})
public class PaletteTemplate extends AbstractableTemplate { public class PaletteTemplate extends AbstractableTemplate {
@Value("noise")
@Abstractable
@Default
private NoiseBuilder noise = new NoiseBuilder();
@Value("id") @Value("id")
private String id; private String id;
@@ -16,38 +23,20 @@ public class PaletteTemplate extends AbstractableTemplate {
@Abstractable @Abstractable
private List<PaletteLayer> palette; private List<PaletteLayer> palette;
@Value("simplex") public PaletteTemplate() {
@Abstractable noise.setType(FastNoiseLite.NoiseType.WhiteNoise);
@Default noise.setDimensions(3);
private boolean simplex = false; }
@Value("frequency")
@Abstractable
@Default
private double frequency = 0.02D;
@Value("seed")
@Abstractable
@Default
private long seed = 0;
public String getID() { public String getID() {
return id; return id;
} }
public double getFrequency() {
return frequency;
}
public long getSeed() {
return seed;
}
public List<PaletteLayer> getPalette() { public List<PaletteLayer> getPalette() {
return palette; return palette;
} }
public boolean isSimplex() { public NoiseBuilder getNoise() {
return simplex; return noise;
} }
} }
@@ -114,7 +114,7 @@ public class MasterChunkGenerator implements TerraChunkGenerator {
for(int y = world.getMaxHeight() - 1; y >= 0; y--) { for(int y = world.getMaxHeight() - 1; y >= 0; y--) {
if(sampler.sample(x, y, z) > 0) { if(sampler.sample(x, y, z) > 0) {
justSet = true; 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); chunk.setBlock(x, y, z, data);
if(paletteLevel == 0 && c.doSlabs() && y < 255) { if(paletteLevel == 0 && c.doSlabs() && y < 255) {
prepareBlockPartFloor(data, chunk.getBlockData(x, y + 1, z), chunk, new Vector3(x, y + 1, z), c.getSlabPalettes(), 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++; paletteLevel++;
} else if(y <= sea) { } 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()) { if(justSet && c.doSlabs()) {
prepareBlockPartCeiling(data, chunk.getBlockData(x, y, z), chunk, new Vector3(x, y, z), c.getSlabPalettes(), c.getStairPalettes(), c.getSlabThreshold(), sampler); 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, 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) { 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) { if(stairs != null) {
Palette<BlockData> stairPalette = stairs.get(down.getMaterial()); Palette<BlockData> stairPalette = stairs.get(down.getMaterial());
if(stairPalette != null) { 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) { if(stair instanceof Stairs) {
Stairs stairNew = (Stairs) stair; Stairs stairNew = (Stairs) stair;
if(placeStair(orig, chunk, block, thresh, sampler, stairNew)) return; // Successfully placed part. 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) { if(slab instanceof Waterlogged) {
((Waterlogged) slab).setWaterlogged(orig.matches(water)); ((Waterlogged) slab).setWaterlogged(orig.matches(water));
} else if(orig.matches(water)) return; } 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, 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) { 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) { if(stairs != null) {
Palette<BlockData> stairPalette = stairs.get(up.getMaterial()); Palette<BlockData> stairPalette = stairs.get(up.getMaterial());
if(stairPalette != null) { 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) { if(stair instanceof Stairs) {
Stairs stairNew = (Stairs) stair.clone(); Stairs stairNew = (Stairs) stair.clone();
stairNew.setHalf(Bisected.Half.TOP); 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 Bisected) ((Bisected) slab).setHalf(Bisected.Half.TOP);
if(slab instanceof Slab) ((Slab) slab).setType(Slab.Type.TOP); if(slab instanceof Slab) ((Slab) slab).setType(Slab.Type.TOP);
if(slab instanceof Waterlogged) { 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) { 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); 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); 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); 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); stairNew.setFacing(BlockFace.EAST);
} else stairNew = null; } else stairNew = null;
if(stairNew != null) { if(stairNew != null) {
@@ -4,8 +4,11 @@ import com.dfsek.tectonic.annotations.Default;
import com.dfsek.tectonic.annotations.Value; import com.dfsek.tectonic.annotations.Value;
import com.dfsek.tectonic.config.ConfigTemplate; import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite; 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.NoiseSampler;
import com.dfsek.terra.api.math.noise.samplers.Normalizer;
@SuppressWarnings("FieldMayBeFinal")
public class NoiseBuilder implements ConfigTemplate { public class NoiseBuilder implements ConfigTemplate {
@Value("type") @Value("type")
@Default @Default
@@ -39,7 +42,7 @@ public class NoiseBuilder implements ConfigTemplate {
@Default @Default
private double weightedStrength = 0.0D; private double weightedStrength = 0.0D;
@Value("offset") @Value("salt")
@Default @Default
private int seedOffset = 0; private int seedOffset = 0;
@@ -71,6 +74,22 @@ public class NoiseBuilder implements ConfigTemplate {
@Default @Default
private int dimensions = 2; 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) { public NoiseSampler build(int seed) {
FastNoiseLite noise = new FastNoiseLite(seed + seedOffset); FastNoiseLite noise = new FastNoiseLite(seed + seedOffset);
if(!fractalType.equals(FastNoiseLite.FractalType.None)) { if(!fractalType.equals(FastNoiseLite.FractalType.None)) {
@@ -85,6 +104,7 @@ public class NoiseBuilder implements ConfigTemplate {
noise.setCellularDistanceFunction(cellularDistanceFunction); noise.setCellularDistanceFunction(cellularDistanceFunction);
noise.setCellularReturnType(cellularReturnType); noise.setCellularReturnType(cellularReturnType);
noise.setCellularJitter(cellularJitter); noise.setCellularJitter(cellularJitter);
if(lookup != null) noise.setCellularNoiseLookup(lookup.build(seed));
} }
noise.setNoiseType(type); noise.setNoiseType(type);
@@ -95,6 +115,7 @@ public class NoiseBuilder implements ConfigTemplate {
noise.setRotationType3D(rotationType3D); noise.setRotationType3D(rotationType3D);
noise.setFrequency(frequency); noise.setFrequency(frequency);
if(!normalType.equals(Normalizer.NormalType.NONE)) return new LinearNormalizer(noise, linearMin, linearMax);
return noise; return noise;
} }
@@ -55,7 +55,7 @@ public class FloraPopulator implements TerraBlockPopulator {
if(entry.getValue().size() <= iter) continue; if(entry.getValue().size() <= iter) continue;
finished = false; finished = false;
FloraLayer layer = entry.getValue().get(iter); 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++; iter++;
} }
@@ -42,7 +42,7 @@ public class TreePopulator implements TerraBlockPopulator {
UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z, GenerationPhase.POPULATE); UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z, GenerationPhase.POPULATE);
for(TreeLayer layer : biome.getConfig().getTrees()) { for(TreeLayer layer : biome.getConfig().getTrees()) {
if(layer.getDensity() >= random.nextDouble() * 100) 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)));
} }
} }
} }
@@ -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.math.vector.Vector2;
import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.Chunk;
import java.util.Random;
public abstract class PlaceableLayer<T> { public abstract class PlaceableLayer<T> {
protected final double density; protected final double density;
protected final Range level; protected final Range level;
@@ -37,5 +35,5 @@ public abstract class PlaceableLayer<T> {
return layer; return layer;
} }
public abstract void place(Chunk chunk, Vector2 coords, Random random); public abstract void place(Chunk chunk, Vector2 coords);
} }
@@ -8,8 +8,6 @@ import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.world.flora.Flora; import com.dfsek.terra.api.world.flora.Flora;
import com.dfsek.terra.population.items.PlaceableLayer; import com.dfsek.terra.population.items.PlaceableLayer;
import java.util.Random;
public class FloraLayer extends PlaceableLayer<Flora> { public class FloraLayer extends PlaceableLayer<Flora> {
public FloraLayer(double density, Range level, ProbabilityCollection<Flora> layer, NoiseSampler noise) { public FloraLayer(double density, Range level, ProbabilityCollection<Flora> layer, NoiseSampler noise) {
@@ -21,8 +19,8 @@ public class FloraLayer extends PlaceableLayer<Flora> {
} }
@Override @Override
public void place(Chunk chunk, Vector2 coords, Random random) { public void place(Chunk chunk, Vector2 coords) {
Flora item = noise == null ? layer.get(random) : layer.get(noise, (chunk.getX() << 4) + coords.getX(), (chunk.getZ() << 4) + coords.getZ()); 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())); item.getValidSpawnsAt(chunk, (int) coords.getX(), (int) coords.getZ(), level).forEach(block -> item.plant(block.getLocation()));
} }
} }
@@ -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 for(int i = 0; FastMath.abs(i) < size; i += c) { // Down if ceiling, up if floor
int lvl = (FastMath.abs(i)); 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(doRotation) {
if(data instanceof Directional) { if(data instanceof Directional) {
((Directional) data).setFacing(oneFace); ((Directional) data).setFacing(oneFace);
@@ -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.platform.world.Chunk;
import com.dfsek.terra.api.world.tree.Tree; import com.dfsek.terra.api.world.tree.Tree;
import com.dfsek.terra.population.items.PlaceableLayer; import com.dfsek.terra.population.items.PlaceableLayer;
import com.dfsek.terra.util.PopulationUtil;
import java.util.Random;
public class TreeLayer extends PlaceableLayer<Tree> { public class TreeLayer extends PlaceableLayer<Tree> {
@@ -19,13 +18,13 @@ public class TreeLayer extends PlaceableLayer<Tree> {
} }
@Override @Override
public void place(Chunk chunk, Vector2 coords, Random random) { public void place(Chunk chunk, Vector2 coords) {
Tree item = layer.get(random); Tree item = layer.get(noise, coords.getX(), coords.getZ());
Block current = chunk.getBlock((int) coords.getX(), level.getMax(), (int) coords.getZ()); Block current = chunk.getBlock((int) coords.getX(), level.getMax(), (int) coords.getZ());
for(int ignored : level) { for(int ignored : level) {
current = current.getRelative(BlockFace.DOWN); current = current.getRelative(BlockFace.DOWN);
if(item.getSpawnable().contains(current.getType())) { 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));
} }
} }
} }