customizable palette noise

This commit is contained in:
dfsek 2021-01-11 13:31:19 -07:00
parent 462b6f4198
commit 18d7408f53
11 changed files with 75 additions and 84 deletions

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,19 @@ 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 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 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

@ -1615,25 +1615,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)
+ (0b01111111111L << 52); // 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)
+ (0b01111111111L << 52); // Sign and exponent
return (Double.longBitsToDouble(base) - 1.5) * 2;
}
// Simplex/OpenSimplex2 Noise

View File

@ -4,10 +4,10 @@ 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;
public SimplexPalette(NoiseSampler r) {
public NoisePalette(NoiseSampler r) {
this.r = 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

@ -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));
for(PaletteLayer layer : config.getPalette()) {
palette.add(layer.getLayer(), layer.getSize());
}

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,19 @@ 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);
}
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

@ -6,6 +6,7 @@ import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.terra.api.math.noise.samplers.FastNoiseLite;
import com.dfsek.terra.api.math.noise.samplers.NoiseSampler;
@SuppressWarnings("FieldMayBeFinal")
public class NoiseBuilder implements ConfigTemplate {
@Value("type")
@Default
@ -39,7 +40,7 @@ public class NoiseBuilder implements ConfigTemplate {
@Default
private double weightedStrength = 0.0D;
@Value("offset")
@Value("salt")
@Default
private int seedOffset = 0;
@ -71,6 +72,10 @@ public class NoiseBuilder implements ConfigTemplate {
@Default
private int dimensions = 2;
@Value("cellular.lookup")
@Default
private NoiseBuilder lookup = new NoiseBuilder();
public NoiseSampler build(int seed) {
FastNoiseLite noise = new FastNoiseLite(seed + seedOffset);
if(!fractalType.equals(FastNoiseLite.FractalType.None)) {
@ -85,6 +90,7 @@ public class NoiseBuilder implements ConfigTemplate {
noise.setCellularDistanceFunction(cellularDistanceFunction);
noise.setCellularReturnType(cellularReturnType);
noise.setCellularJitter(cellularJitter);
noise.setCellularNoiseLookup(lookup.build(seed));
}
noise.setNoiseType(type);